diff --git a/scripts/build/place-binary.cmd b/scripts/build/place-binary.cmd new file mode 100644 index 0000000000..143d4a9eb9 --- /dev/null +++ b/scripts/build/place-binary.cmd @@ -0,0 +1,24 @@ +@echo off + +REM Copyright (c) .NET Foundation and contributors. All rights reserved. +REM Licensed under the MIT license. See LICENSE file in the project root for full license information. + +set SRC=%1 + +set SRC=%SRC:/=\% + +pushd %~dp0..\.. +set DST=%CD%\artifacts\win7-x64\stage2\bin +popd + +if not exist "%SRC%" goto end +if not exist "%DST%" goto skip + +xcopy /F /Y /I "%SRC%" "%DST%" + +goto end + +:skip +echo The destination "%DST%" does not exist. This script is only designed to update a previous full build! + +:end diff --git a/scripts/build/place-binary.sh b/scripts/build/place-binary.sh new file mode 100644 index 0000000000..b80caa4525 --- /dev/null +++ b/scripts/build/place-binary.sh @@ -0,0 +1,33 @@ +#!/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 +SCRIPT_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" +REPOROOT="$( cd -P "$DIR/../.." && pwd )" + +source "$SCRIPT_DIR/../_common.sh" + +echo "Copy From: $1" +echo " To: $STAGE2_DIR/bin/" + +src=${1//\\//} +dst=$STAGE2_DIR/bin/ + +if [ ! -d "$dst" ]; then + mkdir -p $dst +fi + +# Copy the files, if they exist +if ls $src 1> /dev/null 2>&1; then + cp $src $dst + rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi +fi diff --git a/src/Microsoft.DotNet.Cli.Utils/Command.cs b/src/Microsoft.DotNet.Cli.Utils/Command.cs index 843e61070e..53a00db23a 100644 --- a/src/Microsoft.DotNet.Cli.Utils/Command.cs +++ b/src/Microsoft.DotNet.Cli.Utils/Command.cs @@ -170,6 +170,12 @@ public CommandResult Execute() _stdErrCapture?.GetStringBuilder()?.ToString()); } + public Command WorkingDirectory(string projectDirectory) + { + _process.StartInfo.WorkingDirectory = projectDirectory; + return this; + } + public Command EnvironmentVariable(string name, string value) { _process.StartInfo.Environment[name] = value; diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Chain.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Chain.cs new file mode 100644 index 0000000000..fa066a7f5a --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Chain.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.DotNet.Cli.Utils.CommandParsing +{ + internal struct Chain + { + public Chain(TLeft left, TDown down) + : this() + { + Left = left; + Down = down; + } + + public readonly TLeft Left; + public readonly TDown Down; + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/CommandGrammar.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/CommandGrammar.cs new file mode 100644 index 0000000000..5b6e06bd26 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/CommandGrammar.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.DotNet.Cli.Utils.CommandParsing +{ + internal class CommandGrammar : Grammar + { + private CommandGrammar(Func variable, bool preserveSurroundingQuotes) + { + var environmentVariablePiece = Ch('%').And(Rep(Ch().Not(Ch('%')))).And(Ch('%')).Left().Down().Str() + .Build(key => variable(key) ?? "%" + key + "%"); + + var escapeSequencePiece = + Ch('%').And(Ch('%')).Build(_=>"%") + .Or(Ch('^').And(Ch('^')).Build(_ => "^")) + .Or(Ch('\\').And(Ch('\\')).Build(_ => "\\")) + .Or(Ch('\\').And(Ch('\"')).Build(_ => "\"")) + ; + + var specialPiece = environmentVariablePiece.Or(escapeSequencePiece); + + var unquotedPiece = Rep1(Ch().Not(specialPiece).Not(Ch(' '))).Str(); + + var quotedPiece = Rep1(Ch().Not(specialPiece).Not(Ch('\"'))).Str(); + + var unquotedTerm = Rep1(unquotedPiece.Or(specialPiece)).Str(); + + var quotedTerm = Ch('\"').And(Rep(quotedPiece.Or(specialPiece)).Str()).And(Ch('\"')).Left().Down(); + if (preserveSurroundingQuotes) + { + // Str() value assigned to quotedTerm does not include quotation marks surrounding the quoted or + // special piece. Add those quotes back if requested. + quotedTerm = quotedTerm.Build(str => "\"" + str + "\""); + } + + var whitespace = Rep(Ch(' ')); + + var term = whitespace.And(quotedTerm.Or(unquotedTerm)).And(whitespace).Left().Down(); + + Parse = Rep(term); + } + + public readonly Parser> Parse; + + public static string[] Process(string text, Func variables, bool preserveSurroundingQuotes) + { + var grammar = new CommandGrammar(variables, preserveSurroundingQuotes); + var cursor = new Cursor(text, 0, text.Length); + + var result = grammar.Parse(cursor); + if (!result.Remainder.IsEnd) + { + throw new ArgumentException($"Malformed command text '{text}'", nameof(text)); + } + return result.Value.ToArray(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Cursor.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Cursor.cs new file mode 100644 index 0000000000..b8868b80eb --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Cursor.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.DotNet.Cli.Utils.CommandParsing +{ + internal struct Cursor + { + private readonly string _text; + private readonly int _start; + private readonly int _end; + + public Cursor(string text, int start, int end) + { + _text = text; + _start = start; + _end = end; + } + + public bool IsEnd + { + get { return _start == _end; } + } + + public char Peek(int index) + { + return (index + _start) >= _end ? (char)0 : _text[index + _start]; + } + + public Result Advance(TValue result, int length) + { + return new Result(result, new Cursor(_text, _start + length, _end)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Grammar.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Grammar.cs new file mode 100644 index 0000000000..6c2961fb7a --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Grammar.cs @@ -0,0 +1,52 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.DotNet.Cli.Utils.CommandParsing +{ + internal class Grammar + { + protected static Parser> Rep1(Parser parser) + { + Parser> rep = Rep(parser); + return pos => + { + var result = rep(pos); + return result.IsEmpty || !result.Value.Any() ? Result>.Empty : result; + }; + } + + protected static Parser> Rep(Parser parser) + { + return pos => + { + var data = new List(); + for (; ; ) + { + var result = parser(pos); + if (result.IsEmpty) break; + data.Add(result.Value); + pos = result.Remainder; + } + return new Result>(data, pos); + }; + } + + protected static Parser Ch() + { + return pos => pos.IsEnd ? Result.Empty : pos.Advance(pos.Peek(0), 1); + } + + private static Parser IsEnd() + { + return pos => pos.IsEnd ? pos.Advance(true, 0) : Result.Empty; + } + + protected static Parser Ch(char ch) + { + return pos => pos.Peek(0) != ch ? Result.Empty : pos.Advance(ch, 1); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Parser.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Parser.cs new file mode 100644 index 0000000000..b1643f33af --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Parser.cs @@ -0,0 +1,7 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.DotNet.Cli.Utils.CommandParsing +{ + internal delegate Result Parser(Cursor cursor); +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/ParserExtensions.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/ParserExtensions.cs new file mode 100644 index 0000000000..e9bc9ef160 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/ParserExtensions.cs @@ -0,0 +1,85 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.DotNet.Cli.Utils.CommandParsing +{ + internal static class ParserExtensions + { + public static Parser> And(this Parser parser1, + Parser parser2) + { + return pos => + { + var result1 = parser1(pos); + if (result1.IsEmpty) return Result>.Empty; + var result2 = parser2(result1.Remainder); + if (result2.IsEmpty) return Result>.Empty; + return result2.AsValue(new Chain(result1.Value, result2.Value)); + }; + } + + public static Parser Or(this Parser parser1, Parser parser2) + { + return pos => + { + var result1 = parser1(pos); + if (!result1.IsEmpty) return result1; + var result2 = parser2(pos); + if (!result2.IsEmpty) return result2; + return Result.Empty; + }; + } + + public static Parser Not(this Parser parser1, Parser parser2) + { + return pos => + { + var result2 = parser2(pos); + if (!result2.IsEmpty) return Result.Empty; + return parser1(pos); + }; + } + + public static Parser Left(this Parser> parser) + { + return pos => + { + var result = parser(pos); + return result.IsEmpty ? Result.Empty : result.AsValue(result.Value.Left); + }; + } + + public static Parser Down(this Parser> parser) + { + return pos => + { + var result = parser(pos); + return result.IsEmpty ? Result.Empty : result.AsValue(result.Value.Down); + }; + } + + public static Parser Build(this Parser parser, Func builder) + { + return pos => + { + var result = parser(pos); + if (result.IsEmpty) return Result.Empty; + return result.AsValue(builder(result.Value)); + }; + } + + public static Parser Str(this Parser> parser) + { + return parser.Build(x => new string(x.ToArray())); + } + + public static Parser Str(this Parser> parser) + { + return parser.Build(x => String.Concat(x.ToArray())); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Result.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Result.cs new file mode 100644 index 0000000000..4a6adab072 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Result.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.DotNet.Cli.Utils.CommandParsing +{ + internal struct Result + { + public Result(TValue value, Cursor remainder) + : this() + { + Value = value; + Remainder = remainder; + } + + public readonly TValue Value; + public readonly Cursor Remainder; + + public bool IsEmpty + { + get { return Equals(this, default(Result)); } + } + + public static Result Empty + { + get { return default(Result); } + } + + public Result AsValue(TValue2 value2) + { + return new Result(value2, Remainder); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandResult.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResult.cs index f3e2a70cae..c49c40c002 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandResult.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResult.cs @@ -7,6 +7,8 @@ namespace Microsoft.DotNet.Cli.Utils { public struct CommandResult { + public static readonly CommandResult Empty = new CommandResult(); + public int ExitCode { get; } public string StdOut { get; } public string StdErr { get; } diff --git a/src/Microsoft.DotNet.Cli.Utils/DictionaryExtensions.cs b/src/Microsoft.DotNet.Cli.Utils/DictionaryExtensions.cs new file mode 100644 index 0000000000..380cfa6587 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/DictionaryExtensions.cs @@ -0,0 +1,17 @@ +using System.Linq; + +namespace System.Collections.Generic +{ + internal static class DictionaryExtensions + { + public static IEnumerable GetOrEmpty(this IDictionary> self, K key) + { + IEnumerable val; + if (!self.TryGetValue(key, out val)) + { + return Enumerable.Empty(); + } + return val; + } + } +} diff --git a/src/Microsoft.DotNet.Cli.Utils/ScriptExecutor.cs b/src/Microsoft.DotNet.Cli.Utils/ScriptExecutor.cs new file mode 100644 index 0000000000..1d61288a32 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/ScriptExecutor.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.DotNet.Cli.Utils.CommandParsing; +using Microsoft.Extensions.ProjectModel; + +namespace Microsoft.DotNet.Cli.Utils +{ + internal static class ScriptExecutor + { + public static Command CreateCommandForScript(Project project, string scriptCommandLine, IDictionary variables) + { + return CreateCommandForScript(project, scriptCommandLine, WrapVariableDictionary(variables)); + } + + public static Command CreateCommandForScript(Project project, string scriptCommandLine, Func getVariable) + { + // Preserve quotation marks around arguments since command is about to be passed to a shell. May need + // the quotes to ensure the shell groups arguments correctly. + var scriptArguments = CommandGrammar.Process( + scriptCommandLine, + GetScriptVariable(project, getVariable), + preserveSurroundingQuotes: true); + + // Ensure the array won't be empty and the elements won't be null or empty strings. + scriptArguments = scriptArguments.Where(argument => !string.IsNullOrEmpty(argument)).ToArray(); + if (scriptArguments.Length == 0) + { + return null; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Only forward slashes are used in script blocks. Replace with backslashes to correctly + // locate the script. The directory separator is platform-specific. + scriptArguments[0] = scriptArguments[0].Replace( + Path.AltDirectorySeparatorChar, + Path.DirectorySeparatorChar); + + // Command-lines on Windows are executed via "cmd /S /C" in order to support batch files, &&, + // built-in commands like echo, et cetera. /S allows quoting the command as well as the arguments. + // ComSpec is Windows-specific, and contains the full path to cmd.exe + var comSpec = Environment.GetEnvironmentVariable("ComSpec"); + if (!string.IsNullOrEmpty(comSpec)) + { + scriptArguments = + new[] { comSpec, "/S", "/C", "\"" } + .Concat(scriptArguments) + .Concat(new[] { "\"" }) + .ToArray(); + } + } + else + { + // Special-case a script name that, perhaps with added .sh, matches an existing file. + var surroundWithQuotes = false; + var scriptCandidate = scriptArguments[0]; + if (scriptCandidate.StartsWith("\"", StringComparison.Ordinal) && + scriptCandidate.EndsWith("\"", StringComparison.Ordinal)) + { + // Strip surrounding quotes; they were required in project.json to keep the script name + // together but confuse File.Exists() e.g. "My Script", lacking ./ prefix and .sh suffix. + surroundWithQuotes = true; + scriptCandidate = scriptCandidate.Substring(1, scriptCandidate.Length - 2); + } + + if (!scriptCandidate.EndsWith(".sh", StringComparison.Ordinal)) + { + scriptCandidate = scriptCandidate + ".sh"; + } + + if (File.Exists(Path.Combine(project.ProjectDirectory, scriptCandidate))) + { + // scriptCandidate may be a path relative to the project root. If so, likely will not be found + // in the $PATH; add ./ to let bash know where to look. + var prefix = Path.IsPathRooted(scriptCandidate) ? string.Empty : "./"; + var quote = surroundWithQuotes ? "\"" : string.Empty; + scriptArguments[0] = $"{ quote }{ prefix }{ scriptCandidate }{ quote }"; + } + + // Always use /usr/bin/env bash -c in order to support redirection and so on; similar to Windows case. + // Unlike Windows, must escape quotation marks within the newly-quoted string. + scriptArguments = new[] { "/usr/bin/env", "bash", "-c", "\"" } + .Concat(scriptArguments.Select(argument => argument.Replace("\"", "\\\""))) + .Concat(new[] { "\"" }) + .ToArray(); + } + + return Command.Create(scriptArguments.FirstOrDefault(), string.Join(" ", scriptArguments.Skip(1))) + .WorkingDirectory(project.ProjectDirectory); + } + private static Func WrapVariableDictionary(IDictionary contextVariables) + { + return key => + { + string value; + contextVariables.TryGetValue(key, out value); + return value; + }; + } + + private static Func GetScriptVariable(Project project, Func getVariable) + { + var keys = new Dictionary>(StringComparer.OrdinalIgnoreCase) + { + { "project:Directory", () => project.ProjectDirectory }, + { "project:Name", () => project.Name }, + { "project:Version", () => project.Version.ToString() }, + }; + + return key => + { + // try returning key from dictionary + Func valueFactory; + if (keys.TryGetValue(key, out valueFactory)) + { + return valueFactory(); + } + + // try returning command-specific key + var value = getVariable(key); + if (!string.IsNullOrEmpty(value)) + { + return value; + } + + // try returning environment variable + return Environment.GetEnvironmentVariable(key); + }; + } + } +} diff --git a/src/Microsoft.DotNet.Cli.Utils/ScriptNames.cs b/src/Microsoft.DotNet.Cli.Utils/ScriptNames.cs new file mode 100644 index 0000000000..c095c5cdf4 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/ScriptNames.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.DotNet.Cli.Utils +{ + internal static class ScriptNames + { + public const string PreCompile = "precompile"; + public const string PostCompile = "postcompile"; + } +} diff --git a/src/Microsoft.DotNet.Cli.Utils/project.json b/src/Microsoft.DotNet.Cli.Utils/project.json index 919670b12a..f05494eec9 100644 --- a/src/Microsoft.DotNet.Cli.Utils/project.json +++ b/src/Microsoft.DotNet.Cli.Utils/project.json @@ -4,12 +4,14 @@ "shared": "**/*.cs", "dependencies": { - "System.AppContext": "4.0.0-beta-23504", + "System.AppContext": "4.0.0", "System.Console": "4.0.0-beta-23504", "System.IO.FileSystem": "4.0.1-beta-23504", "System.Threading": "4.0.11-beta-23504", "System.Diagnostics.Process": "4.1.0-beta-23504", - "System.Runtime.InteropServices.RuntimeInformation": "4.0.0-beta-23504" + "System.Runtime.InteropServices.RuntimeInformation": "4.0.0-beta-23504", + + "Microsoft.DotNet.ProjectModel": { "target": "project" } }, "frameworks": { "dnxcore50": { } diff --git a/src/Microsoft.DotNet.Cli/project.json b/src/Microsoft.DotNet.Cli/project.json index 2e3e77ecd3..7e28db3c23 100644 --- a/src/Microsoft.DotNet.Cli/project.json +++ b/src/Microsoft.DotNet.Cli/project.json @@ -20,5 +20,11 @@ }, "frameworks": { "dnxcore50": { } + }, + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] } } diff --git a/src/Microsoft.DotNet.Compiler.Common/project.json b/src/Microsoft.DotNet.Compiler.Common/project.json index 4ff854e2c4..c4f20af74d 100644 --- a/src/Microsoft.DotNet.Compiler.Common/project.json +++ b/src/Microsoft.DotNet.Compiler.Common/project.json @@ -1,14 +1,20 @@ { - "version": "1.0.0-*", + "version": "1.0.0-*", - "dependencies": { - "System.CommandLine": "0.1.0-*", - "System.Linq": "4.0.1-beta-23504", - "System.Runtime": "4.0.21-beta-23504", - "Microsoft.DotNet.ProjectModel": "1.0.0-*" - }, + "dependencies": { + "System.CommandLine": "0.1.0-*", + "System.Linq": "4.0.1-beta-23504", + "System.Runtime": "4.0.21-beta-23504", + "Microsoft.DotNet.ProjectModel": "1.0.0-*" + }, - "frameworks": { - "dnxcore50": { } - } + "frameworks": { + "dnxcore50": { } + }, + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] + } } diff --git a/src/Microsoft.DotNet.ProjectModel/Project.cs b/src/Microsoft.DotNet.ProjectModel/Project.cs index 0af81dc9af..44dfc92e9d 100644 --- a/src/Microsoft.DotNet.ProjectModel/Project.cs +++ b/src/Microsoft.DotNet.ProjectModel/Project.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using Microsoft.Extensions.ProjectModel.Files; using Microsoft.Extensions.ProjectModel.Graph; using NuGet.Frameworks; diff --git a/src/Microsoft.DotNet.ProjectModel/project.json b/src/Microsoft.DotNet.ProjectModel/project.json index 6121eadd87..8746aa1c48 100644 --- a/src/Microsoft.DotNet.ProjectModel/project.json +++ b/src/Microsoft.DotNet.ProjectModel/project.json @@ -1,30 +1,36 @@ { - "version": "1.0.0-*", - "description": "Types to model a .NET Project", - "dependencies": { - "Microsoft.CSharp": "4.0.1-beta-23504", - "System.Collections": "4.0.11-beta-23504", - "System.Linq": "4.0.1-beta-23504", - "System.Threading.Thread": "4.0.0-beta-23504", - "System.Runtime.Loader": "4.0.0-beta-23504", - "System.Dynamic.Runtime": "4.0.11-beta-23504", - "System.Runtime.InteropServices.RuntimeInformation": "4.0.0-beta-23504", - "System.Security.Cryptography.Algorithms": "4.0.0-beta-23504", + "version": "1.0.0-*", + "description": "Types to model a .NET Project", + "dependencies": { + "Microsoft.CSharp": "4.0.1-beta-23504", + "System.Collections": "4.0.11-beta-23504", + "System.Linq": "4.0.1-beta-23504", + "System.Threading.Thread": "4.0.0-beta-23504", + "System.Runtime.Loader": "4.0.0-beta-23504", + "System.Dynamic.Runtime": "4.0.11-beta-23504", + "System.Runtime.InteropServices.RuntimeInformation": "4.0.0-beta-23504", + "System.Security.Cryptography.Algorithms": "4.0.0-beta-23504", - "NuGet.Packaging": "3.3.0-*", + "NuGet.Packaging": "3.3.0-*", - "Microsoft.Extensions.FileSystemGlobbing": "1.0.0-rc2-15791", - "Microsoft.Extensions.JsonParser.Sources": { - "type": "build", - "version": "1.0.0-*" + "Microsoft.Extensions.FileSystemGlobbing": "1.0.0-rc2-15791", + "Microsoft.Extensions.JsonParser.Sources": { + "type": "build", + "version": "1.0.0-*" + }, + "Microsoft.Extensions.HashCodeCombiner.Sources": { + "type": "build", + "version": "1.0.0-*" + } }, - "Microsoft.Extensions.HashCodeCombiner.Sources": { - "type": "build", - "version": "1.0.0-*" - } - }, - "frameworks": { - "dnxcore50": { } - } + "frameworks": { + "dnxcore50": { } + }, + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] + } } diff --git a/src/Microsoft.DotNet.Tools.Compiler.Csc/project.json b/src/Microsoft.DotNet.Tools.Compiler.Csc/project.json index c5691145d6..93068d4f8a 100644 --- a/src/Microsoft.DotNet.Tools.Compiler.Csc/project.json +++ b/src/Microsoft.DotNet.Tools.Compiler.Csc/project.json @@ -1,28 +1,34 @@ { - "name": "dotnet-compile-csc", - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - "dependencies": { - "System.Console": "4.0.0-beta-23504", - "System.Collections": "4.0.11-beta-23504", - "System.Linq": "4.0.1-beta-23504", - "System.Diagnostics.Process": "4.1.0-beta-23504", - "System.IO.FileSystem": "4.0.1-beta-23504", + "name": "dotnet-compile-csc", + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "System.Console": "4.0.0-beta-23504", + "System.Collections": "4.0.11-beta-23504", + "System.Linq": "4.0.1-beta-23504", + "System.Diagnostics.Process": "4.1.0-beta-23504", + "System.IO.FileSystem": "4.0.1-beta-23504", - "Microsoft.DotNet.Cli.Utils": { - "type": "build", - "version": "1.0.0-*" + "Microsoft.DotNet.Cli.Utils": { + "type": "build", + "version": "1.0.0-*" + }, + "Microsoft.Extensions.CommandLineUtils.Sources": { + "type": "build", + "version": "1.0.0-*" + }, + "Microsoft.DotNet.Compiler.Common": "1.0.0-*", + "Microsoft.Net.Compilers.netcore": "1.2.0-beta-20151117-04" }, - "Microsoft.Extensions.CommandLineUtils.Sources": { - "type": "build", - "version": "1.0.0-*" + "frameworks": { + "dnxcore50": { } }, - "Microsoft.DotNet.Compiler.Common": "1.0.0-*", - "Microsoft.Net.Compilers.netcore": "1.2.0-beta-20151117-04" - }, - "frameworks": { - "dnxcore50": { } - } + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] + } } diff --git a/src/Microsoft.DotNet.Tools.Compiler.Native/project.json b/src/Microsoft.DotNet.Tools.Compiler.Native/project.json index b1592ead09..8a71ee3a71 100644 --- a/src/Microsoft.DotNet.Tools.Compiler.Native/project.json +++ b/src/Microsoft.DotNet.Tools.Compiler.Native/project.json @@ -5,12 +5,12 @@ "emitEntryPoint": true }, "dependencies": { - "Microsoft.NETCore.Platforms":"1.0.1-beta-23504", + "Microsoft.NETCore.Platforms": "1.0.1-beta-23504", "Microsoft.NETCore.Runtime": "1.0.1-beta-23504", "System.Console": "4.0.0-beta-23504", "System.Collections": "4.0.11-beta-23504", - "System.Linq": "4.0.1-beta-23504", - "Microsoft.NETCore.TestHost": "1.0.0-beta-23504", + "System.Linq": "4.0.1-beta-23504", + "Microsoft.NETCore.TestHost": "1.0.0-beta-23504", "System.Diagnostics.Process": "4.1.0-beta-23504", "System.IO": "4.0.11-beta-23504", "System.IO.FileSystem": "4.0.1-beta-23504", @@ -23,11 +23,17 @@ "type": "build", "version": "1.0.0-*" }, - "Microsoft.DotNet.ILCompiler": "1.0.1-*", - "Microsoft.DotNet.ObjectWriter": "1.0.2-*", - "Microsoft.DotNet.RyuJit": "1.0.0-*" + "Microsoft.DotNet.ILCompiler": "1.0.1-*", + "Microsoft.DotNet.ObjectWriter": "1.0.2-*", + "Microsoft.DotNet.RyuJit": "1.0.0-*" }, "frameworks": { "dnxcore50": { } + }, + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] } } diff --git a/src/Microsoft.DotNet.Tools.Compiler/Program.cs b/src/Microsoft.DotNet.Tools.Compiler/Program.cs index 863961b7ff..f7dc21ceed 100644 --- a/src/Microsoft.DotNet.Tools.Compiler/Program.cs +++ b/src/Microsoft.DotNet.Tools.Compiler/Program.cs @@ -230,6 +230,11 @@ private static bool Compile(ProjectContext context, string configuration, string projects.Clear(); } + return CompileProject(context, configuration, outputPath, intermediateOutputPath, dependencies); + } + + private static bool CompileProject(ProjectContext context, string configuration, string outputPath, string intermediateOutputPath, List dependencies) + { Reporter.Output.WriteLine($"Compiling {context.RootProject.Identity.Name.Yellow()} for {context.TargetFramework.DotNetFrameworkName.Yellow()}"); var sw = Stopwatch.StartNew(); @@ -257,9 +262,7 @@ private static bool Compile(ProjectContext context, string configuration, string } // Dump dependency data - // TODO: Turn on only if verbose, we can look at the response - // file anyways - // ShowDependencyInfo(dependencies); + ShowDependencyInfo(dependencies); // Get compilation options var outputName = GetProjectOutput(context.ProjectFile, context.TargetFramework, configuration, outputPath); @@ -278,7 +281,7 @@ private static bool Compile(ProjectContext context, string configuration, string // Resolve full path to key file compilationOptions.KeyFile = Path.GetFullPath(Path.Combine(context.ProjectFile.ProjectDirectory, compilationOptions.KeyFile)); } - + // Add compilation options to the args compilerArgs.AddRange(compilationOptions.SerializeToArgs()); @@ -317,33 +320,46 @@ private static bool Compile(ProjectContext context, string configuration, string var rsp = Path.Combine(intermediateOutputPath, $"dotnet-compile.{context.ProjectFile.Name}.rsp"); File.WriteAllLines(rsp, compilerArgs); + // Run pre-compile event + var contextVariables = new Dictionary() + { + { "compile:TargetFramework", context.TargetFramework.DotNetFrameworkName }, + { "compile:Configuration", configuration }, + { "compile:OutputFile", outputName }, + { "compile:OutputDir", outputPath }, + { "compile:ResponseFile", rsp } + }; + RunScripts(context, ScriptNames.PreCompile, contextVariables); + var result = Command.Create($"dotnet-compile-{compilerName}", $"@\"{rsp}\"") - .OnErrorLine(line => - { - var diagnostic = ParseDiagnostic(context.ProjectDirectory, line); - if (diagnostic != null) - { - diagnostics.Add(diagnostic); - } - else - { - Reporter.Error.WriteLine(line); - } - }) - .OnOutputLine(line => - { - var diagnostic = ParseDiagnostic(context.ProjectDirectory, line); - - if (diagnostic != null) - { - diagnostics.Add(diagnostic); - } - else - { - Reporter.Output.WriteLine(line); - } - }) - .Execute(); + .OnErrorLine(line => + { + var diagnostic = ParseDiagnostic(context.ProjectDirectory, line); + if (diagnostic != null) + { + diagnostics.Add(diagnostic); + } + else + { + Reporter.Error.WriteLine(line); + } + }) + .OnOutputLine(line => + { + var diagnostic = ParseDiagnostic(context.ProjectDirectory, line); + if (diagnostic != null) + { + diagnostics.Add(diagnostic); + } + else + { + Reporter.Output.WriteLine(line); + } + }).Execute(); + + // Run post-compile event + contextVariables["compile:CompilerExitCode"] = result.ExitCode.ToString(); + RunScripts(context, ScriptNames.PostCompile, contextVariables); var success = result.ExitCode == 0; @@ -358,6 +374,17 @@ private static bool Compile(ProjectContext context, string configuration, string return PrintSummary(diagnostics, sw, success); } + private static void RunScripts(ProjectContext context, string name, Dictionary contextVariables) + { + foreach (var script in context.ProjectFile.Scripts.GetOrEmpty(name)) + { + ScriptExecutor.CreateCommandForScript(context.ProjectFile, script, contextVariables) + .ForwardStdErr() + .ForwardStdOut() + .Execute(); + } + } + private static string GetProjectOutput(Project project, NuGetFramework framework, string configuration, string outputPath) { var compilationOptions = project.GetCompilerOptions(framework, configuration); @@ -663,7 +690,7 @@ private static void PrintDiagnostics(List diagnostics) PrintDiagnostic(diag); } } - + private static void PrintDiagnostic(DiagnosticMessage diag) { switch (diag.Severity) @@ -682,28 +709,31 @@ private static void PrintDiagnostic(DiagnosticMessage diag) private static void ShowDependencyInfo(IEnumerable dependencies) { - foreach (var dependency in dependencies) + if (CommandContext.IsVerbose()) { - if (!dependency.Library.Resolved) - { - Reporter.Error.WriteLine($" Unable to resolve dependency {dependency.Library.Identity.ToString().Red().Bold()}"); - Reporter.Error.WriteLine(""); - } - else + foreach (var dependency in dependencies) { - Reporter.Output.WriteLine($" Using {dependency.Library.Identity.Type.Value.Cyan().Bold()} dependency {dependency.Library.Identity.ToString().Cyan().Bold()}"); - Reporter.Output.WriteLine($" Path: {dependency.Library.Path}"); - - foreach (var metadataReference in dependency.CompilationAssemblies) + if (!dependency.Library.Resolved) { - Reporter.Output.WriteLine($" Assembly: {metadataReference}"); + Reporter.Verbose.WriteLine($" Unable to resolve dependency {dependency.Library.Identity.ToString().Red().Bold()}"); + Reporter.Verbose.WriteLine(""); } - - foreach (var sourceReference in dependency.SourceReferences) + else { - Reporter.Output.WriteLine($" Source: {sourceReference}"); + Reporter.Verbose.WriteLine($" Using {dependency.Library.Identity.Type.Value.Cyan().Bold()} dependency {dependency.Library.Identity.ToString().Cyan().Bold()}"); + Reporter.Verbose.WriteLine($" Path: {dependency.Library.Path}"); + + foreach (var metadataReference in dependency.CompilationAssemblies) + { + Reporter.Verbose.WriteLine($" Assembly: {metadataReference}"); + } + + foreach (var sourceReference in dependency.SourceReferences) + { + Reporter.Verbose.WriteLine($" Source: {sourceReference}"); + } + Reporter.Verbose.WriteLine(""); } - Reporter.Output.WriteLine(""); } } } diff --git a/src/Microsoft.DotNet.Tools.Compiler/project.json b/src/Microsoft.DotNet.Tools.Compiler/project.json index 8c3f5a91e4..f07f2694c5 100644 --- a/src/Microsoft.DotNet.Tools.Compiler/project.json +++ b/src/Microsoft.DotNet.Tools.Compiler/project.json @@ -25,5 +25,11 @@ }, "frameworks": { "dnxcore50": { } + }, + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] } } diff --git a/src/Microsoft.DotNet.Tools.Init/project.json b/src/Microsoft.DotNet.Tools.Init/project.json index eecec76ddb..f3f9813500 100644 --- a/src/Microsoft.DotNet.Tools.Init/project.json +++ b/src/Microsoft.DotNet.Tools.Init/project.json @@ -1,5 +1,5 @@ { - "name" : "dotnet-init", + "name": "dotnet-init", "version": "1.0.0-*", "compilationOptions": { "emitEntryPoint": true @@ -25,8 +25,14 @@ } }, "compileExclude": [ "Template/**" ], - "resource": [ "Template/**" ], + "resource": [ "Template/**" ], "frameworks": { "dnxcore50": { } + }, + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] } } diff --git a/src/Microsoft.DotNet.Tools.Publish/project.json b/src/Microsoft.DotNet.Tools.Publish/project.json index 7044ef53bd..10a0082bf0 100644 --- a/src/Microsoft.DotNet.Tools.Publish/project.json +++ b/src/Microsoft.DotNet.Tools.Publish/project.json @@ -26,5 +26,11 @@ }, "frameworks": { "dnxcore50": { } + }, + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] } } diff --git a/src/Microsoft.DotNet.Tools.Repl.Csi/project.json b/src/Microsoft.DotNet.Tools.Repl.Csi/project.json index 87a314d0ec..1a0b3e9090 100644 --- a/src/Microsoft.DotNet.Tools.Repl.Csi/project.json +++ b/src/Microsoft.DotNet.Tools.Repl.Csi/project.json @@ -1,28 +1,34 @@ { - "name": "dotnet-repl-csi", - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - "dependencies": { - "System.Console": "4.0.0-beta-23504", - "System.Collections": "4.0.11-beta-23504", - "System.Linq": "4.0.1-beta-23504", - "System.Diagnostics.Process": "4.1.0-beta-23504", - "System.IO.FileSystem": "4.0.1-beta-23504", - "System.AppContext": "4.0.1-beta-23504", + "name": "dotnet-repl-csi", + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "System.Console": "4.0.0-beta-23504", + "System.Collections": "4.0.11-beta-23504", + "System.Linq": "4.0.1-beta-23504", + "System.Diagnostics.Process": "4.1.0-beta-23504", + "System.IO.FileSystem": "4.0.1-beta-23504", + "System.AppContext": "4.0.1-beta-23504", - "Microsoft.DotNet.Cli.Utils": { - "type": "build", - "version": "1.0.0-*" + "Microsoft.DotNet.Cli.Utils": { + "type": "build", + "version": "1.0.0-*" + }, + "Microsoft.Extensions.CommandLineUtils.Sources": { + "type": "build", + "version": "1.0.0-*" + }, + "Microsoft.Net.CSharp.Interactive.netcore": "1.2.0-beta-20151106-02" }, - "Microsoft.Extensions.CommandLineUtils.Sources": { - "type": "build", - "version": "1.0.0-*" + "frameworks": { + "dnxcore50": { } }, - "Microsoft.Net.CSharp.Interactive.netcore": "1.2.0-beta-20151106-02" - }, - "frameworks": { - "dnxcore50": { } - } + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] + } } diff --git a/src/Microsoft.DotNet.Tools.Repl/project.json b/src/Microsoft.DotNet.Tools.Repl/project.json index 429ce5d32a..5a3fd98542 100644 --- a/src/Microsoft.DotNet.Tools.Repl/project.json +++ b/src/Microsoft.DotNet.Tools.Repl/project.json @@ -1,27 +1,33 @@ { - "name": "dotnet-repl", - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - "dependencies": { - "System.Console": "4.0.0-beta-23504", - "System.Collections": "4.0.11-beta-23504", - "System.Linq": "4.0.1-beta-23504", - "System.Diagnostics.Process": "4.1.0-beta-23504", - "System.IO.FileSystem": "4.0.1-beta-23504", + "name": "dotnet-repl", + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "System.Console": "4.0.0-beta-23504", + "System.Collections": "4.0.11-beta-23504", + "System.Linq": "4.0.1-beta-23504", + "System.Diagnostics.Process": "4.1.0-beta-23504", + "System.IO.FileSystem": "4.0.1-beta-23504", - "Microsoft.DotNet.ProjectModel": "1.0.0-*", - "Microsoft.DotNet.Cli.Utils": { - "type": "build", - "version": "1.0.0-*" + "Microsoft.DotNet.ProjectModel": "1.0.0-*", + "Microsoft.DotNet.Cli.Utils": { + "type": "build", + "version": "1.0.0-*" + }, + "Microsoft.Extensions.CommandLineUtils.Sources": { + "type": "build", + "version": "1.0.0-*" + } + }, + "frameworks": { + "dnxcore50": { } }, - "Microsoft.Extensions.CommandLineUtils.Sources": { - "type": "build", - "version": "1.0.0-*" + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] } - }, - "frameworks": { - "dnxcore50": { } - } } diff --git a/src/Microsoft.DotNet.Tools.Resgen/project.json b/src/Microsoft.DotNet.Tools.Resgen/project.json index 0a1028451e..710529940b 100644 --- a/src/Microsoft.DotNet.Tools.Resgen/project.json +++ b/src/Microsoft.DotNet.Tools.Resgen/project.json @@ -24,5 +24,11 @@ }, "frameworks": { "dnxcore50": { } + }, + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] } } diff --git a/src/Microsoft.DotNet.Tools.Run/project.json b/src/Microsoft.DotNet.Tools.Run/project.json index 520aacb1bb..2d75f21b59 100644 --- a/src/Microsoft.DotNet.Tools.Run/project.json +++ b/src/Microsoft.DotNet.Tools.Run/project.json @@ -1,28 +1,34 @@ { - "name": "dotnet-run", - "version": "1.0.0-*", - "compilationOptions": { - "emitEntryPoint": true - }, - "dependencies": { - "System.Console": "4.0.0-beta-23504", - "System.Collections": "4.0.11-beta-23504", - "System.Linq": "4.0.1-beta-23504", - "System.Diagnostics.Process": "4.1.0-beta-23504", - "System.IO.FileSystem": "4.0.1-beta-23504", + "name": "dotnet-run", + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "System.Console": "4.0.0-beta-23504", + "System.Collections": "4.0.11-beta-23504", + "System.Linq": "4.0.1-beta-23504", + "System.Diagnostics.Process": "4.1.0-beta-23504", + "System.IO.FileSystem": "4.0.1-beta-23504", - "Microsoft.DotNet.ProjectModel": "1.0.0-*", - "Microsoft.DotNet.Cli.Utils": { - "type": "build", - "version": "1.0.0-*" + "Microsoft.DotNet.ProjectModel": "1.0.0-*", + "Microsoft.DotNet.Cli.Utils": { + "type": "build", + "version": "1.0.0-*" + }, + "Microsoft.Extensions.CommandLineUtils.Sources": { + "type": "build", + "version": "1.0.0-*" + }, + "Microsoft.Net.Compilers.netcore": "1.2.0-beta-20151117-04" }, - "Microsoft.Extensions.CommandLineUtils.Sources": { - "type": "build", - "version": "1.0.0-*" + "frameworks": { + "dnxcore50": { } }, - "Microsoft.Net.Compilers.netcore": "1.2.0-beta-20151117-04" - }, - "frameworks": { - "dnxcore50": { } - } + "scripts": { + "postcompile": [ + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"", + "../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\"" + ] + } }