Packaged Commands
This commit is contained in:
parent
ecda2a8106
commit
5cd64066c4
38 changed files with 805 additions and 360 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -262,3 +262,4 @@ _Pvt_Extensions
|
||||||
# Exceptions
|
# Exceptions
|
||||||
# Build Scripts
|
# Build Scripts
|
||||||
!scripts/build/
|
!scripts/build/
|
||||||
|
test/PackagedCommands/Consumers/*/project.json
|
||||||
|
|
|
@ -34,9 +34,8 @@ else {
|
||||||
header "Compiling"
|
header "Compiling"
|
||||||
_ "$RepoRoot\scripts\compile\compile.ps1" @("$Configuration")
|
_ "$RepoRoot\scripts\compile\compile.ps1" @("$Configuration")
|
||||||
|
|
||||||
# Put stage2 on the PATH now that we have a build
|
header "Setting Stage2 as PATH and DOTNET_TOOLS"
|
||||||
$env:PATH = "$Stage2Dir\bin;$env:PATH"
|
setPathAndHome "$Stage2Dir"
|
||||||
$env:DOTNET_HOME = "$Stage2Dir"
|
|
||||||
|
|
||||||
header "Running Tests"
|
header "Running Tests"
|
||||||
_ "$RepoRoot\scripts\test\runtests.ps1"
|
_ "$RepoRoot\scripts\test\runtests.ps1"
|
||||||
|
|
|
@ -37,12 +37,11 @@ fi
|
||||||
header "Compiling"
|
header "Compiling"
|
||||||
$REPOROOT/scripts/compile/compile.sh
|
$REPOROOT/scripts/compile/compile.sh
|
||||||
|
|
||||||
# Put stage2 on the PATH now that we have a build
|
header "Setting Stage2 as PATH, DOTNET_HOME, and DOTNET_TOOLS"
|
||||||
export DOTNET_TOOLS=$STAGE1_DIR
|
export DOTNET_HOME=$STAGE2_DIR && export DOTNET_TOOLS=$STAGE2DIR && export PATH=$STAGE2_DIR/bin:$PATH
|
||||||
export PATH=$STAGE2_DIR/bin:$PATH
|
|
||||||
|
|
||||||
header "Testing stage2..."
|
header "Running Tests"
|
||||||
DOTNET_HOME=$STAGE2_DIR DOTNET_TOOLS=$STAGE2_DIR $REPOROOT/scripts/test/runtests.sh
|
$REPOROOT/scripts/test/runtests.sh
|
||||||
|
|
||||||
header "Validating Dependencies"
|
header "Validating Dependencies"
|
||||||
$REPOROOT/scripts/test/validate-dependencies.sh
|
$REPOROOT/scripts/test/validate-dependencies.sh
|
||||||
|
|
|
@ -20,4 +20,4 @@ $PackageDir = "$RepoRoot\artifacts\packages\dnvm"
|
||||||
setEnvIfDefault "DOTNET_INSTALL_DIR" "$(Convert-Path "$PSScriptRoot\..")\.dotnet_stage0\win7-x64"
|
setEnvIfDefault "DOTNET_INSTALL_DIR" "$(Convert-Path "$PSScriptRoot\..")\.dotnet_stage0\win7-x64"
|
||||||
setEnvIfDefault "DOTNET_BUILD_VERSION" "0.1.0.0"
|
setEnvIfDefault "DOTNET_BUILD_VERSION" "0.1.0.0"
|
||||||
setPathAndHomeIfDefault "$Stage2Dir"
|
setPathAndHomeIfDefault "$Stage2Dir"
|
||||||
setEnvIfDefault "CONFIGURATION" "Debug"
|
setVarIfDefault "Configuration" "Debug"
|
||||||
|
|
|
@ -26,13 +26,26 @@ function setEnvIfDefault([string]$envVarName, [string]$value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setVarIfDefault([string]$varName, [string]$value)
|
||||||
|
{
|
||||||
|
If (-not (Get-Variable -name $varName -ErrorAction SilentlyContinue))
|
||||||
|
{
|
||||||
|
Set-Variable -name $varName -value $value -scope 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setPathAndHomeIfDefault([string]$rootPath)
|
function setPathAndHomeIfDefault([string]$rootPath)
|
||||||
{
|
{
|
||||||
If ($env:DOTNET_HOME -eq $null)
|
If ($env:DOTNET_HOME -eq $null)
|
||||||
{
|
{
|
||||||
|
setPathAndHome $rootPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPathAndHome([string]$rootPath)
|
||||||
|
{
|
||||||
$env:DOTNET_HOME=$rootPath
|
$env:DOTNET_HOME=$rootPath
|
||||||
$env:PATH="$rootPath\bin;$env:PATH"
|
$env:PATH="$rootPath\bin;$env:PATH"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _([string]$command)
|
function _([string]$command)
|
||||||
|
|
|
@ -11,7 +11,4 @@ $env:PATH = "$env:DOTNET_INSTALL_DIR\cli\bin;$StartPath"
|
||||||
|
|
||||||
_ "$RepoRoot\scripts\compile\compile-stage.ps1" @("$Tfm","$Rid","$Configuration","$Stage1Dir","$RepoRoot","$HostDir")
|
_ "$RepoRoot\scripts\compile\compile-stage.ps1" @("$Tfm","$Rid","$Configuration","$Stage1Dir","$RepoRoot","$HostDir")
|
||||||
|
|
||||||
# Copy dnx into stage 1
|
|
||||||
cp -rec "$DnxRoot\" "$Stage1Dir\bin\dnx\"
|
|
||||||
|
|
||||||
$env:PATH=$StartPath
|
$env:PATH=$StartPath
|
|
@ -23,11 +23,4 @@ export PATH=$DOTNET_INSTALL_DIR/bin:$PATH
|
||||||
header "Building stage1 dotnet using downloaded stage0 ..."
|
header "Building stage1 dotnet using downloaded stage0 ..."
|
||||||
OUTPUT_DIR=$STAGE1_DIR $REPOROOT/scripts/compile/compile-stage.sh
|
OUTPUT_DIR=$STAGE1_DIR $REPOROOT/scripts/compile/compile-stage.sh
|
||||||
|
|
||||||
# Copy DNX in to stage1
|
|
||||||
cp -R $DNX_ROOT $STAGE1_DIR/bin/dnx
|
|
||||||
|
|
||||||
# Copy and CHMOD the dotnet-dnx script
|
|
||||||
cp $REPOROOT/scripts/dotnet-dnx.sh $STAGE1_DIR/bin/dotnet-dnx
|
|
||||||
chmod a+x $STAGE1_DIR/bin/dotnet-dnx
|
|
||||||
|
|
||||||
export PATH=$StartPath
|
export PATH=$StartPath
|
64
scripts/test/package-command-test.ps1
Normal file
64
scripts/test/package-command-test.ps1
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
. "$PSScriptRoot\..\common\_common.ps1"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"v1", "v2" |
|
||||||
|
foreach {
|
||||||
|
dotnet pack --output "$RepoRoot\artifacts\packages" "$RepoRoot\test\PackagedCommands\Commands\dotnet-hello\$_\dotnet-hello"
|
||||||
|
if (!$?) {
|
||||||
|
error "Command failed: dotnet pack"
|
||||||
|
Exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# workaround for dotnet-restore from the root failing for these tests since their dependencies aren't built yet
|
||||||
|
dir "$RepoRoot\test\PackagedCommands\Consumers" | where {$_.PsIsContainer} |
|
||||||
|
foreach {
|
||||||
|
pushd "$RepoRoot\test\PackagedCommands\Consumers\$_"
|
||||||
|
copy project.json.template project.json
|
||||||
|
popd
|
||||||
|
}
|
||||||
|
|
||||||
|
#restore test projects
|
||||||
|
pushd "$RepoRoot\test\PackagedCommands\Consumers"
|
||||||
|
dotnet restore -s "$RepoRoot\artifacts\packages" --no-cache --ignore-failed-sources --parallel
|
||||||
|
if (!$?) {
|
||||||
|
error "Command failed: dotnet restore"
|
||||||
|
Exit 1
|
||||||
|
}
|
||||||
|
popd
|
||||||
|
|
||||||
|
#compile apps
|
||||||
|
dir "$RepoRoot\test\PackagedCommands\Consumers" | where {$_.PsIsContainer} | where {$_.Name.Contains("Direct")} |
|
||||||
|
foreach {
|
||||||
|
pushd "$RepoRoot\test\PackagedCommands\Consumers\$_"
|
||||||
|
dotnet compile
|
||||||
|
popd
|
||||||
|
}
|
||||||
|
|
||||||
|
#run test
|
||||||
|
dir "$RepoRoot\test\PackagedCommands\Consumers" | where {$_.PsIsContainer} | where {$_.Name.Contains("AppWith")} |
|
||||||
|
foreach {
|
||||||
|
$testName = "test\PackagedCommands\Consumers\$_"
|
||||||
|
pushd "$RepoRoot\$testName"
|
||||||
|
$outputArray = dotnet hello | Out-String
|
||||||
|
$output = [string]::Join('\n', $outputArray)
|
||||||
|
|
||||||
|
del "project.json"
|
||||||
|
if ($output -ne "hello") {
|
||||||
|
error "Test Failed: $testName\dotnet hello"
|
||||||
|
error " printed $output"
|
||||||
|
Exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info "Test passed: $testName"
|
||||||
|
popd
|
||||||
|
}
|
||||||
|
|
||||||
|
Exit 0
|
67
scripts/test/package-command-test.sh
Executable file
67
scripts/test/package-command-test.sh
Executable file
|
@ -0,0 +1,67 @@
|
||||||
|
#!/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.
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SOURCE="${BASH_SOURCE[0]}"
|
||||||
|
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
|
||||||
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
SOURCE="$(readlink "$SOURCE")"
|
||||||
|
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
||||||
|
done
|
||||||
|
|
||||||
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
|
||||||
|
source "$DIR/../common/_common.sh"
|
||||||
|
|
||||||
|
dotnet pack --output "$REPOROOT/artifacts/packages" "$REPOROOT/test/PackagedCommands/Commands/dotnet-hello/v1/dotnet-hello"
|
||||||
|
dotnet pack --output "$REPOROOT/artifacts/packages" "$REPOROOT/test/PackagedCommands/Commands/dotnet-hello/v2/dotnet-hello"
|
||||||
|
|
||||||
|
# enable restore for test projects
|
||||||
|
for test in `ls -l "$REPOROOT/test/PackagedCommands/Consumers" | grep ^d | awk '{print $9}' |
|
||||||
|
do
|
||||||
|
pushd "$REPOROOT/test/PackagedCommands/Consumers/$test"
|
||||||
|
cp "project.json.template" "project.json"
|
||||||
|
popd
|
||||||
|
done
|
||||||
|
|
||||||
|
# restore test projects
|
||||||
|
pushd "$REPOROOT/test/PackagedCommands/Consumers"
|
||||||
|
dotnet restore -s "$REPOROOT/artifacts/packages" --no-cache --ignore-failed-sources --parallel
|
||||||
|
popd
|
||||||
|
|
||||||
|
#compile tests with direct dependencies
|
||||||
|
for test in `ls -l "$REPOROOT/test/PackagedCommands/Consumers" | grep ^d | awk '{print $9}' | grep "Direct"`
|
||||||
|
do
|
||||||
|
pushd "$REPOROOT/test/PackagedCommands/Consumers/$test"
|
||||||
|
dotnet compile
|
||||||
|
popd
|
||||||
|
done
|
||||||
|
|
||||||
|
#run test
|
||||||
|
for test in `ls -l "$REPOROOT/test/PackagedCommands/Consumers" | grep ^d | awk '{print $9}' | grep "AppWith"`
|
||||||
|
do
|
||||||
|
testName="test/PackagedCommands/Consumers/$test"
|
||||||
|
|
||||||
|
pushd "$REPOROOT/$testName"
|
||||||
|
|
||||||
|
output=$(dotnet hello)
|
||||||
|
|
||||||
|
rm "project.json"
|
||||||
|
|
||||||
|
if [ $testOutput != "hello" ]
|
||||||
|
then
|
||||||
|
error "Test Failed: $testName/dotnet hello"
|
||||||
|
error " printed $testOutput"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Test Passed: $testName"
|
||||||
|
fi
|
||||||
|
|
||||||
|
popd
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
|
@ -59,6 +59,13 @@ $TestProjects | ForEach-Object {
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
|
& $RepoRoot\scripts\test\package-command-test.ps1
|
||||||
|
$exitCode = $LastExitCode
|
||||||
|
if ($exitCode -ne 0) {
|
||||||
|
$failCount += 1
|
||||||
|
$failingTests += "package-command-test"
|
||||||
|
}
|
||||||
|
|
||||||
if ($failCount -ne 0) {
|
if ($failCount -ne 0) {
|
||||||
Write-Host -ForegroundColor Red "The following tests failed."
|
Write-Host -ForegroundColor Red "The following tests failed."
|
||||||
$failingTests | ForEach-Object {
|
$failingTests | ForEach-Object {
|
||||||
|
|
|
@ -12,6 +12,7 @@ while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symli
|
||||||
SOURCE="$(readlink "$SOURCE")"
|
SOURCE="$(readlink "$SOURCE")"
|
||||||
[[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
[[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
||||||
done
|
done
|
||||||
|
|
||||||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
|
||||||
source "$DIR/../common/_common.sh"
|
source "$DIR/../common/_common.sh"
|
||||||
|
@ -51,6 +52,12 @@ do
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
"$REPOROOT/scripts/test/package-command-test.sh"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
failCount+=1
|
||||||
|
failedTests+=("package-command-test.sh")
|
||||||
|
fi
|
||||||
|
|
||||||
for test in ${failedTests[@]}
|
for test in ${failedTests[@]}
|
||||||
do
|
do
|
||||||
error "$test.dll failed. Logs in '$TestBinRoot/${test}-testResults.xml'"
|
error "$test.dll failed. Logs in '$TestBinRoot/${test}-testResults.xml'"
|
||||||
|
|
|
@ -5,12 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using Microsoft.DotNet.ProjectModel;
|
|
||||||
using NuGet.Frameworks;
|
using NuGet.Frameworks;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli.Utils
|
namespace Microsoft.DotNet.Cli.Utils
|
||||||
|
@ -21,179 +16,48 @@ namespace Microsoft.DotNet.Cli.Utils
|
||||||
private readonly StreamForwarder _stdOut;
|
private readonly StreamForwarder _stdOut;
|
||||||
private readonly StreamForwarder _stdErr;
|
private readonly StreamForwarder _stdErr;
|
||||||
|
|
||||||
public enum CommandResolutionStrategy
|
|
||||||
{
|
|
||||||
//command loaded from a nuget package
|
|
||||||
NugetPackage,
|
|
||||||
|
|
||||||
//command loaded from the same directory as the executing assembly
|
|
||||||
BaseDirectory,
|
|
||||||
|
|
||||||
//command loaded from path
|
|
||||||
Path,
|
|
||||||
|
|
||||||
//command not found
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _running = false;
|
private bool _running = false;
|
||||||
|
|
||||||
private Command(string executable, string args, CommandResolutionStrategy resolutionStrategy)
|
private Command(CommandSpec commandSpec)
|
||||||
{
|
{
|
||||||
// Set the things we need
|
var psi = new ProcessStartInfo
|
||||||
var psi = new ProcessStartInfo()
|
|
||||||
{
|
{
|
||||||
FileName = executable,
|
FileName = commandSpec.Path,
|
||||||
Arguments = args,
|
Arguments = commandSpec.Args,
|
||||||
RedirectStandardError = true,
|
RedirectStandardError = true,
|
||||||
RedirectStandardOutput = true
|
RedirectStandardOutput = true
|
||||||
};
|
};
|
||||||
|
|
||||||
_process = new Process()
|
_stdOut = new StreamForwarder();
|
||||||
|
_stdErr = new StreamForwarder();
|
||||||
|
|
||||||
|
_process = new Process
|
||||||
{
|
{
|
||||||
StartInfo = psi
|
StartInfo = psi
|
||||||
};
|
};
|
||||||
|
|
||||||
_stdOut = new StreamForwarder();
|
ResolutionStrategy = commandSpec.ResolutionStrategy;
|
||||||
_stdErr = new StreamForwarder();
|
|
||||||
|
|
||||||
ResolutionStrategy = resolutionStrategy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Command Create(string executable, IEnumerable<string> args, NuGetFramework framework = null)
|
public static Command Create(string commandName, IEnumerable<string> args, NuGetFramework framework = null)
|
||||||
{
|
{
|
||||||
return Create(executable, string.Join(" ", args), framework);
|
return Create(commandName, string.Join(" ", args), framework);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Command Create(string executable, string args, NuGetFramework framework = null)
|
public static Command Create(string commandName, string args, NuGetFramework framework = null)
|
||||||
{
|
{
|
||||||
|
var commandSpec = CommandResolver.TryResolveCommandSpec(commandName, args, framework);
|
||||||
|
|
||||||
var resolutionStrategy = CommandResolutionStrategy.None;
|
if (commandSpec == null)
|
||||||
|
|
||||||
ResolveExecutablePath(ref executable, ref args, ref resolutionStrategy, framework);
|
|
||||||
|
|
||||||
return new Command(executable, args, resolutionStrategy);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ResolveExecutablePath(ref string executable, ref string args, ref CommandResolutionStrategy resolutionStrategy, NuGetFramework framework = null)
|
|
||||||
{
|
|
||||||
executable =
|
|
||||||
ResolveExecutablePathFromProject(executable, framework, ref resolutionStrategy) ??
|
|
||||||
ResolveExecutableFromPath(executable, ref args, ref resolutionStrategy);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ResolveExecutableFromPath(string executable, ref string args, ref CommandResolutionStrategy resolutionStrategy)
|
|
||||||
{
|
|
||||||
resolutionStrategy = CommandResolutionStrategy.Path;
|
|
||||||
|
|
||||||
foreach (string suffix in Constants.RunnableSuffixes)
|
|
||||||
{
|
{
|
||||||
var fullExecutable = Path.GetFullPath(Path.Combine(
|
throw new CommandUnknownException(commandName);
|
||||||
AppContext.BaseDirectory, executable + suffix));
|
|
||||||
|
|
||||||
if (File.Exists(fullExecutable))
|
|
||||||
{
|
|
||||||
executable = fullExecutable;
|
|
||||||
|
|
||||||
resolutionStrategy = CommandResolutionStrategy.BaseDirectory;
|
|
||||||
|
|
||||||
// In priority order we've found the best runnable extension, so break.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var command = new Command(commandSpec);
|
||||||
|
|
||||||
// On Windows, we want to avoid using "cmd" if possible (it mangles the colors, and a bunch of other things)
|
return command;
|
||||||
// So, do a quick path search to see if we can just directly invoke it
|
|
||||||
var useCmd = ShouldUseCmd(executable);
|
|
||||||
|
|
||||||
if (useCmd)
|
|
||||||
{
|
|
||||||
var comSpec = Environment.GetEnvironmentVariable("ComSpec");
|
|
||||||
// wrap 'executable' within quotes to deal woth space in its path.
|
|
||||||
args = $"/S /C \"\"{executable}\" {args}\"";
|
|
||||||
executable = comSpec;
|
|
||||||
}
|
|
||||||
|
|
||||||
return executable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ResolveExecutablePathFromProject(string executable, NuGetFramework framework, ref CommandResolutionStrategy resolutionStrategy)
|
|
||||||
{
|
|
||||||
if (framework == null) return null;
|
|
||||||
|
|
||||||
var projectRootPath = Directory.GetCurrentDirectory();
|
|
||||||
|
|
||||||
if (!File.Exists(Path.Combine(projectRootPath, Project.FileName))) return null;
|
|
||||||
|
|
||||||
var commandName = Path.GetFileNameWithoutExtension(executable);
|
|
||||||
|
|
||||||
var projectContext = ProjectContext.Create(projectRootPath, framework);
|
|
||||||
|
|
||||||
var commandPackage = projectContext.LibraryManager.GetLibraries()
|
|
||||||
.Where(l => l.GetType() == typeof (PackageDescription))
|
|
||||||
.Select(l => l as PackageDescription)
|
|
||||||
.FirstOrDefault(p =>
|
|
||||||
{
|
|
||||||
var fileNames = p.Library.Files
|
|
||||||
.Select(Path.GetFileName)
|
|
||||||
.Where(n => Path.GetFileNameWithoutExtension(n) == commandName)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return fileNames.Contains(commandName + FileNameSuffixes.DotNet.Exe) &&
|
|
||||||
fileNames.Contains(commandName + FileNameSuffixes.DotNet.DynamicLib) &&
|
|
||||||
fileNames.Contains(commandName + FileNameSuffixes.Deps);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (commandPackage == null) return null;
|
|
||||||
|
|
||||||
var commandPath = commandPackage.Library.Files
|
|
||||||
.First(f => Path.GetFileName(f) == commandName + FileNameSuffixes.DotNet.Exe);
|
|
||||||
|
|
||||||
resolutionStrategy = CommandResolutionStrategy.NugetPackage;
|
|
||||||
|
|
||||||
return Path.Combine(projectContext.PackagesDirectory, commandPackage.Path, commandPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool ShouldUseCmd(string executable)
|
|
||||||
{
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
var extension = Path.GetExtension(executable);
|
|
||||||
if (!string.IsNullOrEmpty(extension))
|
|
||||||
{
|
|
||||||
return !string.Equals(extension, ".exe", StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
else if (executable.Contains(Path.DirectorySeparatorChar))
|
|
||||||
{
|
|
||||||
// It's a relative path without an extension
|
|
||||||
if (File.Exists(executable + ".exe"))
|
|
||||||
{
|
|
||||||
// It refers to an exe!
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Search the path to see if we can find it
|
|
||||||
foreach (var path in Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator))
|
|
||||||
{
|
|
||||||
var candidate = Path.Combine(path, executable + ".exe");
|
|
||||||
if (File.Exists(candidate))
|
|
||||||
{
|
|
||||||
// We found an exe!
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's a non-exe :(
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Non-windows never uses cmd
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandResult Execute()
|
public CommandResult Execute()
|
||||||
{
|
{
|
||||||
Reporter.Verbose.WriteLine($"Running {_process.StartInfo.FileName} {_process.StartInfo.Arguments}");
|
Reporter.Verbose.WriteLine($"Running {_process.StartInfo.FileName} {_process.StartInfo.Arguments}");
|
||||||
|
@ -334,147 +198,4 @@ namespace Microsoft.DotNet.Cli.Utils
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class StreamForwarder
|
|
||||||
{
|
|
||||||
private const int DefaultBufferSize = 256;
|
|
||||||
|
|
||||||
private readonly int _bufferSize;
|
|
||||||
private StringBuilder _builder;
|
|
||||||
private StringWriter _capture;
|
|
||||||
private Action<string> _write;
|
|
||||||
private Action<string> _writeLine;
|
|
||||||
|
|
||||||
public StreamForwarder(int bufferSize = DefaultBufferSize)
|
|
||||||
{
|
|
||||||
_bufferSize = bufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Capture()
|
|
||||||
{
|
|
||||||
if (_capture != null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Already capturing stream!");
|
|
||||||
}
|
|
||||||
_capture = new StringWriter();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetCapturedOutput()
|
|
||||||
{
|
|
||||||
return _capture?.GetStringBuilder()?.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ForwardTo(Action<string> write, Action<string> writeLine)
|
|
||||||
{
|
|
||||||
if (writeLine == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(writeLine));
|
|
||||||
}
|
|
||||||
if (_writeLine != null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Already handling stream!");
|
|
||||||
}
|
|
||||||
_write = write;
|
|
||||||
_writeLine = writeLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Thread BeginRead(TextReader reader)
|
|
||||||
{
|
|
||||||
var thread = new Thread(() => Read(reader)) { IsBackground = true };
|
|
||||||
thread.Start();
|
|
||||||
return thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Read(TextReader reader)
|
|
||||||
{
|
|
||||||
_builder = new StringBuilder();
|
|
||||||
var buffer = new char[_bufferSize];
|
|
||||||
int n;
|
|
||||||
while ((n = reader.Read(buffer, 0, _bufferSize)) > 0)
|
|
||||||
{
|
|
||||||
_builder.Append(buffer, 0, n);
|
|
||||||
WriteBlocks();
|
|
||||||
}
|
|
||||||
WriteRemainder();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteBlocks()
|
|
||||||
{
|
|
||||||
int n = _builder.Length;
|
|
||||||
if (n == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
bool sawReturn = false;
|
|
||||||
for (int i = 0; i < n; i++)
|
|
||||||
{
|
|
||||||
char c = _builder[i];
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case '\r':
|
|
||||||
sawReturn = true;
|
|
||||||
continue;
|
|
||||||
case '\n':
|
|
||||||
WriteLine(_builder.ToString(offset, i - offset - (sawReturn ? 1 : 0)));
|
|
||||||
offset = i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sawReturn = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the buffer contains no line breaks and _write is
|
|
||||||
// supported, send the buffer content.
|
|
||||||
if (!sawReturn &&
|
|
||||||
(offset == 0) &&
|
|
||||||
((_write != null) || (_writeLine == null)))
|
|
||||||
{
|
|
||||||
WriteRemainder();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_builder.Remove(0, offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteRemainder()
|
|
||||||
{
|
|
||||||
if (_builder.Length == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Write(_builder.ToString());
|
|
||||||
_builder.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteLine(string str)
|
|
||||||
{
|
|
||||||
if (_capture != null)
|
|
||||||
{
|
|
||||||
_capture.WriteLine(str);
|
|
||||||
}
|
|
||||||
// If _write is supported, so is _writeLine.
|
|
||||||
if (_writeLine != null)
|
|
||||||
{
|
|
||||||
_writeLine(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Write(string str)
|
|
||||||
{
|
|
||||||
if (_capture != null)
|
|
||||||
{
|
|
||||||
_capture.Write(str);
|
|
||||||
}
|
|
||||||
if (_write != null)
|
|
||||||
{
|
|
||||||
_write(str);
|
|
||||||
}
|
|
||||||
else if (_writeLine != null)
|
|
||||||
{
|
|
||||||
_writeLine(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli.Utils
|
namespace Microsoft.DotNet.Cli.Utils
|
||||||
{
|
{
|
||||||
|
|
17
src/Microsoft.DotNet.Cli.Utils/CommandResolutionStrategy.cs
Normal file
17
src/Microsoft.DotNet.Cli.Utils/CommandResolutionStrategy.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Microsoft.DotNet.Cli.Utils
|
||||||
|
{
|
||||||
|
public enum CommandResolutionStrategy
|
||||||
|
{
|
||||||
|
//command loaded from a nuget package
|
||||||
|
NugetPackage,
|
||||||
|
|
||||||
|
//command loaded from the same directory as the executing assembly
|
||||||
|
BaseDirectory,
|
||||||
|
|
||||||
|
//command loaded from path
|
||||||
|
Path,
|
||||||
|
|
||||||
|
//command not found
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
196
src/Microsoft.DotNet.Cli.Utils/CommandResolver.cs
Normal file
196
src/Microsoft.DotNet.Cli.Utils/CommandResolver.cs
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using NuGet.Frameworks;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
using Microsoft.DotNet.ProjectModel.Graph;
|
||||||
|
using NuGet.Packaging;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Cli.Utils
|
||||||
|
{
|
||||||
|
internal static class CommandResolver
|
||||||
|
{
|
||||||
|
public static CommandSpec TryResolveCommandSpec(string commandName, string args, NuGetFramework framework = null)
|
||||||
|
{
|
||||||
|
return ResolveFromRootedCommand(commandName, args) ??
|
||||||
|
ResolveFromProjectDependencies(commandName, args, framework) ??
|
||||||
|
ResolveFromProjectTools(commandName, args) ??
|
||||||
|
ResolveFromPath(commandName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CommandSpec ResolveFromPath(string commandName, string args)
|
||||||
|
{
|
||||||
|
var commandPath = Env.GetCommandPath(commandName);
|
||||||
|
|
||||||
|
if (commandPath == null) return null;
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
|
||||||
|
Path.GetExtension(commandPath).Equals(".cmd", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var preferredCommandPath = Env.GetCommandPath(commandName, ".exe");
|
||||||
|
|
||||||
|
if (preferredCommandPath != null)
|
||||||
|
{
|
||||||
|
commandPath = Environment.GetEnvironmentVariable("ComSpec");
|
||||||
|
|
||||||
|
args = $"/S /C \"\"{preferredCommandPath}\" {args}\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CommandSpec(commandPath, args, CommandResolutionStrategy.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CommandSpec ResolveFromRootedCommand(string commandName, string args)
|
||||||
|
{
|
||||||
|
if (Path.IsPathRooted(commandName))
|
||||||
|
{
|
||||||
|
return new CommandSpec(commandName, args, CommandResolutionStrategy.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CommandSpec ResolveFromProjectDependencies(string commandName, string args,
|
||||||
|
NuGetFramework framework)
|
||||||
|
{
|
||||||
|
if (framework == null) return null;
|
||||||
|
|
||||||
|
var projectContext = GetProjectContext(framework);
|
||||||
|
|
||||||
|
if (projectContext == null) return null;
|
||||||
|
|
||||||
|
var commandPackage = GetCommandPackage(projectContext, commandName);
|
||||||
|
|
||||||
|
if (commandPackage == null) return null;
|
||||||
|
|
||||||
|
var depsPath = GetDepsPath(projectContext, Constants.DefaultConfiguration);
|
||||||
|
|
||||||
|
return ConfigureCommandFromPackage(commandName, args, commandPackage, projectContext, depsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ProjectContext GetProjectContext(NuGetFramework framework)
|
||||||
|
{
|
||||||
|
var projectRootPath = Directory.GetCurrentDirectory();
|
||||||
|
|
||||||
|
if (!File.Exists(Path.Combine(projectRootPath, Project.FileName)))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var projectContext = ProjectContext.Create(projectRootPath, framework);
|
||||||
|
return projectContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PackageDescription GetCommandPackage(ProjectContext projectContext, string commandName)
|
||||||
|
{
|
||||||
|
return projectContext.LibraryManager.GetLibraries()
|
||||||
|
.Where(l => l.GetType() == typeof (PackageDescription))
|
||||||
|
.Select(l => l as PackageDescription)
|
||||||
|
.FirstOrDefault(p => p.Library.Files
|
||||||
|
.Select(Path.GetFileName)
|
||||||
|
.Where(f => Path.GetFileNameWithoutExtension(f) == commandName)
|
||||||
|
.Select(Path.GetExtension)
|
||||||
|
.Any(e => Env.ExecutableExtensions.Contains(e) ||
|
||||||
|
e == FileNameSuffixes.DotNet.DynamicLib));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CommandSpec ResolveFromProjectTools(string commandName, string args)
|
||||||
|
{
|
||||||
|
var context = GetProjectContext(FrameworkConstants.CommonFrameworks.DnxCore50);
|
||||||
|
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandLibrary = context.ProjectFile.Tools
|
||||||
|
.FirstOrDefault(l => l.Name == commandName);
|
||||||
|
|
||||||
|
if (commandLibrary == default(LibraryRange))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lockPath = Path.Combine(context.ProjectDirectory, "artifacts", "Tools", commandName,
|
||||||
|
"project.lock.json");
|
||||||
|
|
||||||
|
if (!File.Exists(lockPath))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lockFile = LockFileReader.Read(lockPath);
|
||||||
|
|
||||||
|
var lib = lockFile.PackageLibraries.FirstOrDefault(l => l.Name == commandName);
|
||||||
|
var packageDir = new VersionFolderPathResolver(context.PackagesDirectory)
|
||||||
|
.GetInstallPath(lib.Name, lib.Version);
|
||||||
|
|
||||||
|
return Directory.Exists(packageDir)
|
||||||
|
? ConfigureCommandFromPackage(commandName, args, lib.Files, packageDir)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CommandSpec ConfigureCommandFromPackage(string commandName, string args, string packageDir)
|
||||||
|
{
|
||||||
|
var commandPackage = new PackageFolderReader(packageDir);
|
||||||
|
|
||||||
|
var files = commandPackage.GetFiles();
|
||||||
|
|
||||||
|
return ConfigureCommandFromPackage(commandName, args, files, packageDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CommandSpec ConfigureCommandFromPackage(string commandName, string args,
|
||||||
|
PackageDescription commandPackage, ProjectContext projectContext, string depsPath = null)
|
||||||
|
{
|
||||||
|
var files = commandPackage.Library.Files;
|
||||||
|
|
||||||
|
var packageRoot = projectContext.PackagesDirectory;
|
||||||
|
|
||||||
|
var packagePath = commandPackage.Path;
|
||||||
|
|
||||||
|
var packageDir = Path.Combine(packageRoot, packagePath);
|
||||||
|
|
||||||
|
return ConfigureCommandFromPackage(commandName, args, files, packageDir, depsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CommandSpec ConfigureCommandFromPackage(string commandName, string args,
|
||||||
|
IEnumerable<string> files, string packageDir, string depsPath = null)
|
||||||
|
{
|
||||||
|
var fileName = string.Empty;
|
||||||
|
|
||||||
|
var commandPath = files
|
||||||
|
.FirstOrDefault(f => Env.ExecutableExtensions.Contains(Path.GetExtension(f)));
|
||||||
|
|
||||||
|
if (commandPath == null)
|
||||||
|
{
|
||||||
|
var dllPath = files
|
||||||
|
.Where(f => Path.GetFileName(f) == commandName + FileNameSuffixes.DotNet.DynamicLib)
|
||||||
|
.Select(f => Path.Combine(packageDir, f))
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
fileName = CoreHost.Path;
|
||||||
|
|
||||||
|
if (depsPath != null)
|
||||||
|
{
|
||||||
|
args = $"--tpaFile:\"{depsPath}\" {args}";
|
||||||
|
}
|
||||||
|
|
||||||
|
args = $"\"{dllPath}\" {args}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fileName = Path.Combine(packageDir, commandPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CommandSpec(fileName, args, CommandResolutionStrategy.NugetPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetDepsPath(ProjectContext context, string buildConfiguration)
|
||||||
|
{
|
||||||
|
return Path.Combine(context.GetOutputDirectoryPath(buildConfiguration),
|
||||||
|
context.ProjectFile.Name + FileNameSuffixes.Deps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/Microsoft.DotNet.Cli.Utils/CommandSpec.cs
Normal file
18
src/Microsoft.DotNet.Cli.Utils/CommandSpec.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Microsoft.DotNet.Cli.Utils
|
||||||
|
{
|
||||||
|
internal class CommandSpec
|
||||||
|
{
|
||||||
|
public CommandSpec(string path, string args, CommandResolutionStrategy resolutionStrategy)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
Args = args;
|
||||||
|
ResolutionStrategy = resolutionStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Path { get; }
|
||||||
|
|
||||||
|
public string Args { get; }
|
||||||
|
|
||||||
|
public CommandResolutionStrategy ResolutionStrategy { get; }
|
||||||
|
}
|
||||||
|
}
|
19
src/Microsoft.DotNet.Cli.Utils/CommandUnknownException.cs
Normal file
19
src/Microsoft.DotNet.Cli.Utils/CommandUnknownException.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Cli.Utils
|
||||||
|
{
|
||||||
|
public class CommandUnknownException : Exception
|
||||||
|
{
|
||||||
|
public CommandUnknownException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandUnknownException(string commandName) : base($"No executable found matching command \"{commandName}\"")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandUnknownException(string commandName, Exception innerException) : base($"No executable found matching command \"{commandName}\"", innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,6 @@
|
||||||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
// 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.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli.Utils
|
namespace Microsoft.DotNet.Cli.Utils
|
||||||
|
@ -13,12 +9,7 @@ namespace Microsoft.DotNet.Cli.Utils
|
||||||
{
|
{
|
||||||
public static readonly string ProjectFileName = "project.json";
|
public static readonly string ProjectFileName = "project.json";
|
||||||
public static readonly string ExeSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty;
|
public static readonly string ExeSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty;
|
||||||
|
|
||||||
// Priority order of runnable suffixes to look for and run
|
|
||||||
public static readonly string[] RunnableSuffixes = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
|
||||||
? new string[] { ".exe", ".cmd", ".bat" }
|
|
||||||
: new string[] { string.Empty };
|
|
||||||
|
|
||||||
public static readonly string HostExecutableName = "corehost" + ExeSuffix;
|
public static readonly string HostExecutableName = "corehost" + ExeSuffix;
|
||||||
public static readonly string DefaultConfiguration = "Debug";
|
public static readonly string DefaultConfiguration = "Debug";
|
||||||
public static readonly string BinDirectoryName = "bin";
|
public static readonly string BinDirectoryName = "bin";
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.DotNet.Cli.Utils;
|
|
||||||
using Microsoft.DotNet.ProjectModel;
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli.Compiler.Common
|
namespace Microsoft.DotNet.Cli.Utils
|
||||||
{
|
{
|
||||||
internal static class CoreHost
|
public static class CoreHost
|
||||||
{
|
{
|
||||||
internal static string _path;
|
internal static string _path;
|
||||||
|
|
150
src/Microsoft.DotNet.Cli.Utils/StreamForwarder.cs
Normal file
150
src/Microsoft.DotNet.Cli.Utils/StreamForwarder.cs
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Cli.Utils
|
||||||
|
{
|
||||||
|
public sealed class StreamForwarder
|
||||||
|
{
|
||||||
|
private const int DefaultBufferSize = 256;
|
||||||
|
|
||||||
|
private readonly int _bufferSize;
|
||||||
|
private StringBuilder _builder;
|
||||||
|
private StringWriter _capture;
|
||||||
|
private Action<string> _write;
|
||||||
|
private Action<string> _writeLine;
|
||||||
|
|
||||||
|
public StreamForwarder(int bufferSize = DefaultBufferSize)
|
||||||
|
{
|
||||||
|
_bufferSize = bufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Capture()
|
||||||
|
{
|
||||||
|
if (_capture != null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Already capturing stream!");
|
||||||
|
}
|
||||||
|
_capture = new StringWriter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetCapturedOutput()
|
||||||
|
{
|
||||||
|
return _capture?.GetStringBuilder()?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ForwardTo(Action<string> write, Action<string> writeLine)
|
||||||
|
{
|
||||||
|
if (writeLine == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(writeLine));
|
||||||
|
}
|
||||||
|
if (_writeLine != null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Already handling stream!");
|
||||||
|
}
|
||||||
|
_write = write;
|
||||||
|
_writeLine = writeLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Thread BeginRead(TextReader reader)
|
||||||
|
{
|
||||||
|
var thread = new Thread(() => Read(reader)) { IsBackground = true };
|
||||||
|
thread.Start();
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Read(TextReader reader)
|
||||||
|
{
|
||||||
|
_builder = new StringBuilder();
|
||||||
|
var buffer = new char[_bufferSize];
|
||||||
|
int n;
|
||||||
|
while ((n = reader.Read(buffer, 0, _bufferSize)) > 0)
|
||||||
|
{
|
||||||
|
_builder.Append(buffer, 0, n);
|
||||||
|
WriteBlocks();
|
||||||
|
}
|
||||||
|
WriteRemainder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteBlocks()
|
||||||
|
{
|
||||||
|
int n = _builder.Length;
|
||||||
|
if (n == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
bool sawReturn = false;
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
char c = _builder[i];
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '\r':
|
||||||
|
sawReturn = true;
|
||||||
|
continue;
|
||||||
|
case '\n':
|
||||||
|
WriteLine(_builder.ToString(offset, i - offset - (sawReturn ? 1 : 0)));
|
||||||
|
offset = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sawReturn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the buffer contains no line breaks and _write is
|
||||||
|
// supported, send the buffer content.
|
||||||
|
if (!sawReturn &&
|
||||||
|
(offset == 0) &&
|
||||||
|
((_write != null) || (_writeLine == null)))
|
||||||
|
{
|
||||||
|
WriteRemainder();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_builder.Remove(0, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteRemainder()
|
||||||
|
{
|
||||||
|
if (_builder.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Write(_builder.ToString());
|
||||||
|
_builder.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteLine(string str)
|
||||||
|
{
|
||||||
|
if (_capture != null)
|
||||||
|
{
|
||||||
|
_capture.WriteLine(str);
|
||||||
|
}
|
||||||
|
// If _write is supported, so is _writeLine.
|
||||||
|
if (_writeLine != null)
|
||||||
|
{
|
||||||
|
_writeLine(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Write(string str)
|
||||||
|
{
|
||||||
|
if (_capture != null)
|
||||||
|
{
|
||||||
|
_capture.Write(str);
|
||||||
|
}
|
||||||
|
if (_write != null)
|
||||||
|
{
|
||||||
|
_write(str);
|
||||||
|
}
|
||||||
|
else if (_writeLine != null)
|
||||||
|
{
|
||||||
|
_writeLine(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,12 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"dnxcore50": { }
|
"dnxcore50": { }
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"postcompile": [
|
||||||
|
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"",
|
||||||
|
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\""
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,9 +44,25 @@ Common Commands:
|
||||||
{
|
{
|
||||||
DebugHelper.HandleDebugSwitch(ref args);
|
DebugHelper.HandleDebugSwitch(ref args);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ProcessArgs(args);
|
||||||
|
}
|
||||||
|
catch (CommandUnknownException e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.Message);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ProcessArgs(string[] args)
|
||||||
|
{
|
||||||
// CommandLineApplication is a bit restrictive, so we parse things ourselves here. Individual apps should use CLA.
|
// CommandLineApplication is a bit restrictive, so we parse things ourselves here. Individual apps should use CLA.
|
||||||
|
|
||||||
var verbose = false;
|
var verbose = false;
|
||||||
var success = true;
|
var success = true;
|
||||||
var command = string.Empty;
|
var command = string.Empty;
|
||||||
var lastArg = 0;
|
var lastArg = 0;
|
||||||
for (; lastArg < args.Length; lastArg++)
|
for (; lastArg < args.Length; lastArg++)
|
||||||
|
@ -77,7 +93,8 @@ Common Commands:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!success) {
|
if (!success)
|
||||||
|
{
|
||||||
PrintHelp();
|
PrintHelp();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -89,7 +106,7 @@ Common Commands:
|
||||||
return RunHelpCommand(appArgs);
|
return RunHelpCommand(appArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Command.Create("dotnet-" + command, appArgs, new NuGetFramework("DNXCore", Version.Parse("5.0")))
|
return Command.Create("dotnet-" + command, appArgs, FrameworkConstants.CommonFrameworks.DnxCore50)
|
||||||
.EnvironmentVariable(CommandContext.Variables.Verbose, verbose.ToString())
|
.EnvironmentVariable(CommandContext.Variables.Verbose, verbose.ToString())
|
||||||
.EnvironmentVariable(CommandContext.Variables.AnsiPassThru, bool.TrueString)
|
.EnvironmentVariable(CommandContext.Variables.AnsiPassThru, bool.TrueString)
|
||||||
.ForwardStdErr()
|
.ForwardStdErr()
|
||||||
|
|
|
@ -6,7 +6,7 @@ using Microsoft.DotNet.ProjectModel.Compilation;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli.Compiler.Common
|
namespace Microsoft.DotNet.Cli.Compiler.Common
|
||||||
{
|
{
|
||||||
internal static class LibraryExporterExtensions
|
public static class LibraryExporterExtensions
|
||||||
{
|
{
|
||||||
internal static void CopyProjectDependenciesTo(this LibraryExporter exporter, string path, params ProjectDescription[] except)
|
internal static void CopyProjectDependenciesTo(this LibraryExporter exporter, string path, params ProjectDescription[] except)
|
||||||
{
|
{
|
||||||
|
@ -17,7 +17,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
|
||||||
.CopyTo(path);
|
.CopyTo(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void WriteDepsTo(this IEnumerable<LibraryExport> exports, string path)
|
public static void WriteDepsTo(this IEnumerable<LibraryExport> exports, string path)
|
||||||
{
|
{
|
||||||
File.WriteAllLines(path, exports.SelectMany(GenerateLines));
|
File.WriteAllLines(path, exports.SelectMany(GenerateLines));
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,10 +202,5 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
|
||||||
appConfig.Save(stream);
|
appConfig.Save(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetDepsPath(this ProjectContext context, string buildConfiguration)
|
|
||||||
{
|
|
||||||
return Path.Combine(context.GetOutputDirectoryPath(buildConfiguration), context.ProjectFile.Name + ".deps");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,7 +148,7 @@ namespace Microsoft.DotNet.Tools.Build
|
||||||
{
|
{
|
||||||
var pathCommands = CompilerUtil.GetCommandsInvokedByCompile(project)
|
var pathCommands = CompilerUtil.GetCommandsInvokedByCompile(project)
|
||||||
.Select(commandName => Command.Create(commandName, "", project.TargetFramework))
|
.Select(commandName => Command.Create(commandName, "", project.TargetFramework))
|
||||||
.Where(c => Command.CommandResolutionStrategy.Path.Equals(c.ResolutionStrategy));
|
.Where(c => c.ResolutionStrategy.Equals(CommandResolutionStrategy.Path));
|
||||||
|
|
||||||
foreach (var pathCommand in pathCommands)
|
foreach (var pathCommand in pathCommands)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,10 +6,12 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.Dnx.Runtime.Common.CommandLine;
|
||||||
using Microsoft.DotNet.Cli.Compiler.Common;
|
using Microsoft.DotNet.Cli.Compiler.Common;
|
||||||
using Microsoft.DotNet.Cli.Utils;
|
using Microsoft.DotNet.Cli.Utils;
|
||||||
using Microsoft.DotNet.ProjectModel;
|
using Microsoft.DotNet.ProjectModel;
|
||||||
using Microsoft.DotNet.ProjectModel.Compilation;
|
using Microsoft.DotNet.ProjectModel.Compilation;
|
||||||
|
using Microsoft.DotNet.ProjectModel.Graph;
|
||||||
using Microsoft.DotNet.ProjectModel.Utilities;
|
using Microsoft.DotNet.ProjectModel.Utilities;
|
||||||
using NuGet.Frameworks;
|
using NuGet.Frameworks;
|
||||||
using Microsoft.Extensions.DependencyModel;
|
using Microsoft.Extensions.DependencyModel;
|
||||||
|
@ -350,6 +352,17 @@ namespace Microsoft.DotNet.Tools.Compiler
|
||||||
runtimeContext
|
runtimeContext
|
||||||
.MakeCompilationOutputRunnable(outputPath, args.ConfigValue);
|
.MakeCompilationOutputRunnable(outputPath, args.ConfigValue);
|
||||||
}
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(context.ProjectFile.TestRunner))
|
||||||
|
{
|
||||||
|
var projectContext =
|
||||||
|
ProjectContext.Create(context.ProjectDirectory, context.TargetFramework,
|
||||||
|
new[] {RuntimeIdentifier.Current});
|
||||||
|
|
||||||
|
projectContext
|
||||||
|
.CreateExporter(args.ConfigValue)
|
||||||
|
.GetDependencies(LibraryType.Package)
|
||||||
|
.WriteDepsTo(Path.Combine(outputPath, projectContext.ProjectFile.Name + FileNameSuffixes.Deps));
|
||||||
|
}
|
||||||
|
|
||||||
return PrintSummary(diagnostics, sw, success);
|
return PrintSummary(diagnostics, sw, success);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace Microsoft.DotNet.Tools.Restore
|
||||||
{
|
{
|
||||||
var project = ProjectReader.GetProject(restoreTask.ProjectPath);
|
var project = ProjectReader.GetProject(restoreTask.ProjectPath);
|
||||||
|
|
||||||
RestoreTools(project, restoreTask.Arguments);
|
RestoreTools(project, restoreTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
return projectRestoreResult;
|
return projectRestoreResult;
|
||||||
|
@ -60,7 +60,6 @@ namespace Microsoft.DotNet.Tools.Restore
|
||||||
|
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return app.Execute(args);
|
return app.Execute(args);
|
||||||
|
@ -101,33 +100,39 @@ namespace Microsoft.DotNet.Tools.Restore
|
||||||
return firstArg.EndsWith(Project.FileName) && File.Exists(firstArg);
|
return firstArg.EndsWith(Project.FileName) && File.Exists(firstArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RestoreTools(Project project, IEnumerable<string> args)
|
private static void RestoreTools(Project project, RestoreTask restoreTask)
|
||||||
{
|
{
|
||||||
foreach (var tooldep in project.Tools)
|
foreach (var tooldep in project.Tools)
|
||||||
{
|
{
|
||||||
RestoreTool(tooldep, args);
|
RestoreTool(tooldep, restoreTask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RestoreTool(LibraryRange tooldep, IEnumerable<string> args)
|
private static void RestoreTool(LibraryRange tooldep, RestoreTask restoreTask)
|
||||||
{
|
{
|
||||||
var tempPath = Path.Combine(Directory.GetCurrentDirectory(), Guid.NewGuid().ToString(), "bin");
|
var tempPath = Path.Combine(restoreTask.ProjectDirectory, Guid.NewGuid().ToString(), "bin");
|
||||||
|
|
||||||
RestoreToolToPath(tooldep, args, tempPath);
|
RestoreToolToPath(tooldep, restoreTask.Arguments, tempPath);
|
||||||
|
|
||||||
CreateDepsInPackageCache(tooldep, tempPath);
|
CreateDepsInPackageCache(tooldep, tempPath);
|
||||||
|
|
||||||
PersistLockFile(tooldep, tempPath);
|
PersistLockFile(tooldep, tempPath, restoreTask.ProjectDirectory);
|
||||||
|
|
||||||
Directory.Delete(tempPath, true);
|
Directory.Delete(tempPath, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PersistLockFile(LibraryRange tooldep, string tempPath)
|
private static void PersistLockFile(LibraryRange tooldep, string tempPath, string projectPath)
|
||||||
{
|
{
|
||||||
var targetPath = Path.Combine(Directory.GetCurrentDirectory(), "artifacts", "Tools", tooldep.Name);
|
var sourcePath = Path.Combine(tempPath, "project.lock.json");
|
||||||
if (Directory.Exists(targetPath)) Directory.Delete(targetPath, true);
|
var targetDir = Path.Combine(projectPath, "artifacts", "Tools", tooldep.Name);
|
||||||
Directory.CreateDirectory(targetPath);
|
var targetPath = Path.Combine(targetDir, "project.lock.json");
|
||||||
File.Move(Path.Combine(tempPath, "project.lock.json"), Path.Combine(targetPath, "project.lock.json"));
|
|
||||||
|
if (Directory.Exists(targetDir)) Directory.Delete(targetDir, true);
|
||||||
|
Directory.CreateDirectory(targetDir);
|
||||||
|
|
||||||
|
Console.WriteLine($"Writing '{sourcePath}' to '{targetPath}'");
|
||||||
|
|
||||||
|
File.Move(sourcePath, targetPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CreateDepsInPackageCache(LibraryRange toolLibrary, string projectPath)
|
private static void CreateDepsInPackageCache(LibraryRange toolLibrary, string projectPath)
|
||||||
|
@ -156,6 +161,9 @@ namespace Microsoft.DotNet.Tools.Restore
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(tempPath);
|
Directory.CreateDirectory(tempPath);
|
||||||
var projectPath = Path.Combine(tempPath, Project.FileName);
|
var projectPath = Path.Combine(tempPath, Project.FileName);
|
||||||
|
|
||||||
|
Console.WriteLine($"Restoring Tool '{tooldep.Name}' for '{projectPath}' in '{tempPath}'");
|
||||||
|
|
||||||
File.WriteAllText(projectPath, GenerateProjectJsonContents(new[] {"dnxcore50"}));
|
File.WriteAllText(projectPath, GenerateProjectJsonContents(new[] {"dnxcore50"}));
|
||||||
Dnx.RunPackageInstall(tooldep, projectPath, args);
|
Dnx.RunPackageInstall(tooldep, projectPath, args);
|
||||||
Dnx.RunRestore(new [] { $"\"{projectPath}\"", "--runtime", $"{DefaultRid}"}.Concat(args));
|
Dnx.RunRestore(new [] { $"\"{projectPath}\"", "--runtime", $"{DefaultRid}"}.Concat(args));
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Tools.Restore
|
namespace Microsoft.DotNet.Tools.Restore
|
||||||
{
|
{
|
||||||
|
@ -6,6 +9,10 @@ namespace Microsoft.DotNet.Tools.Restore
|
||||||
{
|
{
|
||||||
public string ProjectPath { get; set; }
|
public string ProjectPath { get; set; }
|
||||||
|
|
||||||
public IEnumerable<string> Arguments { get; set; }
|
public IEnumerable<string> Arguments { get; set; }
|
||||||
|
|
||||||
|
public string ProjectDirectory => ProjectPath.EndsWith(Project.FileName, StringComparison.OrdinalIgnoreCase)
|
||||||
|
? Path.GetDirectoryName(ProjectPath)
|
||||||
|
: ProjectPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ConsoleApplication
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Hello");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"compilationOptions": {
|
||||||
|
"emitEntryPoint": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.0.0-rc2-23616"
|
||||||
|
},
|
||||||
|
|
||||||
|
"frameworks": {
|
||||||
|
"dnxcore50": { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ConsoleApplication
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Hello World!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"compilationOptions": {
|
||||||
|
"emitEntryPoint": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.0.0-rc2-23616"
|
||||||
|
},
|
||||||
|
|
||||||
|
"frameworks": {
|
||||||
|
"dnxcore50": { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ConsoleApplication
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Hello World!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"compilationOptions": {
|
||||||
|
"emitEntryPoint": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.0.0-rc2-23616",
|
||||||
|
"dotnet-hello": { "version": "1.0.0", "target": "package" }
|
||||||
|
},
|
||||||
|
|
||||||
|
"frameworks": {
|
||||||
|
"dnxcore50": { }
|
||||||
|
},
|
||||||
|
|
||||||
|
"testRunner": "must-be-specified-to-generate-deps",
|
||||||
|
|
||||||
|
"tools": {
|
||||||
|
"dotnet-hello": { "version": "2.0.0", "target": "package" }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ConsoleApplication
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Hello World!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"compilationOptions": {
|
||||||
|
"emitEntryPoint": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"testRunner": "must-be-specified-to-generate-deps",
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.0.0-rc2-23616",
|
||||||
|
"dotnet-hello": {"version": "1.0.0", "target": "package"}
|
||||||
|
},
|
||||||
|
|
||||||
|
"frameworks": {
|
||||||
|
"dnxcore50": { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ConsoleApplication
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Hello World!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"compilationOptions": {
|
||||||
|
"emitEntryPoint": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.0.0-rc2-23616"
|
||||||
|
},
|
||||||
|
|
||||||
|
"frameworks": {
|
||||||
|
"dnxcore50": { }
|
||||||
|
},
|
||||||
|
|
||||||
|
"tools": {
|
||||||
|
"dotnet-hello": { "version": "1.0.0", "target": "package" }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue