Add Conditional Target capabilities to the build scripts

Make Attribute properties immutable

PR Feedback, bugfixes

PR Feedback
This commit is contained in:
Bryan Thornbury 2016-02-19 17:00:41 -08:00 committed by Bryan
parent adc6aa7eff
commit 6d8b622451
16 changed files with 415 additions and 116 deletions

View file

@ -42,7 +42,13 @@ namespace Microsoft.DotNet.Cli.Build.Framework
BuildTarget target; BuildTarget target;
if (!Targets.TryGetValue(name, out target)) if (!Targets.TryGetValue(name, out target))
{ {
throw new Exception($"Undefined target: {name}"); throw new UndefinedTargetException($"Undefined target: {name}");
}
if (!EvaluateTargetConditions(target))
{
Reporter.Verbose.WriteLine($"Skipping, Target Conditions not met: {target.Name}");
return new BuildTargetResult(target, success: true);
} }
// Check if it's been completed // Check if it's been completed
@ -53,7 +59,6 @@ namespace Microsoft.DotNet.Cli.Build.Framework
return result; return result;
} }
// It hasn't, or we're forcing, so run it // It hasn't, or we're forcing, so run it
result = ExecTarget(target); result = ExecTarget(target);
_completedTargets[target.Name] = result; _completedTargets[target.Name] = result;
@ -80,6 +85,29 @@ namespace Microsoft.DotNet.Cli.Build.Framework
Reporter.Error.WriteLine("error".Red().Bold() + $": {message}"); Reporter.Error.WriteLine("error".Red().Bold() + $": {message}");
} }
private bool EvaluateTargetConditions(BuildTarget target)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
if (target.Conditions == null)
{
return true;
}
foreach (var condition in target.Conditions)
{
if (!condition())
{
return false;
}
}
return true;
}
private BuildTargetResult ExecTarget(BuildTarget target) private BuildTargetResult ExecTarget(BuildTarget target)
{ {
if (target == null) if (target == null)

View file

@ -52,8 +52,6 @@ namespace Microsoft.DotNet.Cli.Build.Framework
public int Run(string[] args) public int Run(string[] args)
{ {
DebugHelper.HandleDebugSwitch(ref args);
var targets = new[] { BuildContext.DefaultTarget }; var targets = new[] { BuildContext.DefaultTarget };
if(args.Length > 0) if(args.Length > 0)
{ {
@ -104,18 +102,40 @@ namespace Microsoft.DotNet.Cli.Build.Framework
private static IEnumerable<BuildTarget> CollectTargets(Type typ) private static IEnumerable<BuildTarget> CollectTargets(Type typ)
{ {
return from m in typ.GetMethods() return from m in typ.GetMethods()
let attr = m.GetCustomAttribute<TargetAttribute>() let targetAttribute = m.GetCustomAttribute<TargetAttribute>()
where attr != null let conditionalAttributes = m.GetCustomAttributes<TargetConditionAttribute>(false)
select CreateTarget(m, attr); where targetAttribute != null
select CreateTarget(m, targetAttribute, conditionalAttributes);
} }
private static BuildTarget CreateTarget(MethodInfo m, TargetAttribute attr) private static BuildTarget CreateTarget(
MethodInfo methodInfo,
TargetAttribute targetAttribute,
IEnumerable<TargetConditionAttribute> targetConditionAttributes)
{ {
var name = targetAttribute.Name ?? methodInfo.Name;
var conditions = ExtractTargetConditionsFromAttributes(targetConditionAttributes);
return new BuildTarget( return new BuildTarget(
attr.Name ?? m.Name, name,
$"{m.DeclaringType.FullName}.{m.Name}", $"{methodInfo.DeclaringType.FullName}.{methodInfo.Name}",
attr.Dependencies, targetAttribute.Dependencies,
(Func<BuildTargetContext, BuildTargetResult>)m.CreateDelegate(typeof(Func<BuildTargetContext, BuildTargetResult>))); conditions,
(Func<BuildTargetContext, BuildTargetResult>)methodInfo.CreateDelegate(typeof(Func<BuildTargetContext, BuildTargetResult>)));
}
private static IEnumerable<Func<bool>> ExtractTargetConditionsFromAttributes(
IEnumerable<TargetConditionAttribute> targetConditionAttributes)
{
if (targetConditionAttributes == null || targetConditionAttributes.Count() == 0)
{
return Enumerable.Empty<Func<bool>>();
}
return targetConditionAttributes
.Select<TargetConditionAttribute, Func<bool>>(c => c.EvaluateCondition)
.ToArray();
} }
private string GenerateSourceString(string file, int? line, string member) private string GenerateSourceString(string file, int? line, string member)

View file

@ -4,21 +4,28 @@ using System.Linq;
namespace Microsoft.DotNet.Cli.Build.Framework namespace Microsoft.DotNet.Cli.Build.Framework
{ {
public class BuildTarget public class BuildTarget
{ {
public string Name { get; } public string Name { get; }
public string Source { get; } public string Source { get; }
public IEnumerable<string> Dependencies { get; } public IEnumerable<string> Dependencies { get; }
public Func<BuildTargetContext, BuildTargetResult> Body { get; } public IEnumerable<Func<bool>> Conditions { get; }
public Func<BuildTargetContext, BuildTargetResult> Body { get; }
public BuildTarget(string name, string source) : this(name, source, Enumerable.Empty<string>(), null) { } public BuildTarget(string name, string source) : this(name, source, Enumerable.Empty<string>(), Enumerable.Empty<Func<bool>>(), null) { }
public BuildTarget(string name, string source, IEnumerable<string> dependencies) : this(name, source, dependencies, null) { } public BuildTarget(string name, string source, IEnumerable<string> dependencies) : this(name, source, dependencies, Enumerable.Empty<Func<bool>>(), null) { }
public BuildTarget(string name, string source, IEnumerable<string> dependencies, Func<BuildTargetContext, BuildTargetResult> body) public BuildTarget(
{ string name,
Name = name; string source,
IEnumerable<string> dependencies,
IEnumerable<Func<bool>> conditions,
Func<BuildTargetContext, BuildTargetResult> body)
{
Name = name;
Source = source; Source = source;
Dependencies = dependencies; Dependencies = dependencies;
Body = body; Conditions = conditions;
} Body = body;
} }
}
} }

View file

@ -25,6 +25,7 @@ namespace Microsoft.DotNet.Cli.Build.Framework
private Action<string> _stdErrHandler; private Action<string> _stdErrHandler;
private bool _running = false; private bool _running = false;
private bool _quietBuildReporter = false;
private Command(string executable, string args) private Command(string executable, string args)
{ {
@ -148,6 +149,12 @@ namespace Microsoft.DotNet.Cli.Build.Framework
return this; return this;
} }
public Command QuietBuildReporter()
{
_quietBuildReporter = true;
return this;
}
public CommandResult Execute() public CommandResult Execute()
{ {
ThrowIfRunning(); ThrowIfRunning();
@ -172,7 +179,7 @@ namespace Microsoft.DotNet.Cli.Build.Framework
_process.EnableRaisingEvents = true; _process.EnableRaisingEvents = true;
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
BuildReporter.BeginSection("EXEC", FormatProcessInfo(_process.StartInfo)); ReportExecBegin();
_process.Start(); _process.Start();
@ -190,15 +197,7 @@ namespace Microsoft.DotNet.Cli.Build.Framework
var exitCode = _process.ExitCode; var exitCode = _process.ExitCode;
var message = $"{FormatProcessInfo(_process.StartInfo)} exited with {exitCode}"; ReportExecEnd(exitCode);
if (exitCode == 0)
{
BuildReporter.EndSection("EXEC", message.Green(), success: true);
}
else
{
BuildReporter.EndSection("EXEC", message.Red().Bold(), success: false);
}
return new CommandResult( return new CommandResult(
_process.StartInfo, _process.StartInfo,
@ -299,6 +298,30 @@ namespace Microsoft.DotNet.Cli.Build.Framework
return info.FileName + " " + info.Arguments; return info.FileName + " " + info.Arguments;
} }
private void ReportExecBegin()
{
if (!_quietBuildReporter)
{
BuildReporter.BeginSection("EXEC", FormatProcessInfo(_process.StartInfo));
}
}
private void ReportExecEnd(int exitCode)
{
if (!_quietBuildReporter)
{
var message = $"{FormatProcessInfo(_process.StartInfo)} exited with {exitCode}";
if (exitCode == 0)
{
BuildReporter.EndSection("EXEC", message.Green(), success: true);
}
else
{
BuildReporter.EndSection("EXEC", message.Red().Bold(), success: false);
}
}
}
private void ThrowIfRunning([CallerMemberName] string memberName = null) private void ThrowIfRunning([CallerMemberName] string memberName = null)
{ {
if (_running) if (_running)

View file

@ -0,0 +1,50 @@
using System;
using Microsoft.Extensions.PlatformAbstractions;
namespace Microsoft.DotNet.Cli.Build.Framework
{
public static class CurrentArchitecture
{
public static BuildArchitecture Current
{
get
{
return DetermineCurrentArchitecture();
}
}
public static bool Isx86
{
get
{
var archName = PlatformServices.Default.Runtime.RuntimeArchitecture;
return string.Equals(archName, "x86", StringComparison.OrdinalIgnoreCase);
}
}
public static bool Isx64
{
get
{
var archName = PlatformServices.Default.Runtime.RuntimeArchitecture;
return string.Equals(archName, "x64", StringComparison.OrdinalIgnoreCase);
}
}
private static BuildArchitecture DetermineCurrentArchitecture()
{
if (Isx86)
{
return BuildArchitecture.x86;
}
else if (Isx64)
{
return BuildArchitecture.x64;
}
else
{
return default(BuildArchitecture);
}
}
}
}

View file

@ -2,47 +2,74 @@ using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Extensions.PlatformAbstractions; using Microsoft.Extensions.PlatformAbstractions;
public static class CurrentPlatform namespace Microsoft.DotNet.Cli.Build.Framework
{ {
public static bool IsWindows public static class CurrentPlatform
{ {
get public static BuildPlatform Current
{ {
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); get
{
return DetermineCurrentPlatform();
}
} }
}
public static bool IsOSX public static bool IsWindows
{
get
{ {
return RuntimeInformation.IsOSPlatform(OSPlatform.OSX); get
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
}
} }
}
public static bool IsLinux public static bool IsOSX
{
get
{ {
return RuntimeInformation.IsOSPlatform(OSPlatform.Linux); get
{
return RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
}
} }
}
public static bool IsUbuntu public static bool IsUbuntu
{
get
{ {
var osname = PlatformServices.Default.Runtime.OperatingSystem; get
return string.Equals(osname, "ubuntu", StringComparison.OrdinalIgnoreCase); {
var osname = PlatformServices.Default.Runtime.OperatingSystem;
return string.Equals(osname, "ubuntu", StringComparison.OrdinalIgnoreCase);
}
} }
}
public static bool IsCentOS public static bool IsCentOS
{
get
{ {
var osname = PlatformServices.Default.Runtime.OperatingSystem; get
return string.Equals(osname, "centos", StringComparison.OrdinalIgnoreCase); {
var osname = PlatformServices.Default.Runtime.OperatingSystem;
return string.Equals(osname, "centos", StringComparison.OrdinalIgnoreCase);
}
}
private static BuildPlatform DetermineCurrentPlatform()
{
if (IsWindows)
{
return BuildPlatform.Windows;
}
else if (IsOSX)
{
return BuildPlatform.OSX;
}
else if (IsUbuntu)
{
return BuildPlatform.Ubuntu;
}
else if (IsCentOS)
{
return BuildPlatform.CentOS;
}
else
{
return default(BuildPlatform);
}
} }
} }
} }

View file

@ -0,0 +1,8 @@
namespace Microsoft.DotNet.Cli.Build.Framework
{
public enum BuildArchitecture
{
x86 = 1,
x64 = 2
}
}

View file

@ -0,0 +1,10 @@
namespace Microsoft.DotNet.Cli.Build.Framework
{
public enum BuildPlatform
{
Windows = 1,
OSX = 2,
Ubuntu = 3,
CentOS = 4
}
}

View file

@ -5,8 +5,8 @@ using System.Linq;
namespace Microsoft.DotNet.Cli.Build.Framework namespace Microsoft.DotNet.Cli.Build.Framework
{ {
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class TargetAttribute : Attribute public class TargetAttribute : Attribute
{ {
public string Name { get; set; } public string Name { get; set; }
public IEnumerable<string> Dependencies { get; } public IEnumerable<string> Dependencies { get; }
@ -20,5 +20,5 @@ namespace Microsoft.DotNet.Cli.Build.Framework
{ {
Dependencies = dependencies; Dependencies = dependencies;
} }
} }
} }

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
namespace Microsoft.DotNet.Cli.Build.Framework
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class BuildArchitecturesAttribute : TargetConditionAttribute
{
private IEnumerable<BuildArchitecture> _buildArchitectures;
public BuildArchitecturesAttribute(params BuildArchitecture[] architectures)
{
if (architectures == null)
{
throw new ArgumentNullException("architectures");
}
_buildArchitectures = architectures;
}
public override bool EvaluateCondition()
{
var currentArchitecture = CurrentArchitecture.Current;
if (currentArchitecture == default(BuildArchitecture))
{
throw new Exception("Unrecognized Architecture");
}
foreach (var architecture in _buildArchitectures)
{
if (architecture == currentArchitecture)
{
return true;
}
}
return false;
}
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
namespace Microsoft.DotNet.Cli.Build.Framework
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class BuildPlatformsAttribute : TargetConditionAttribute
{
private IEnumerable<BuildPlatform> _buildPlatforms;
public BuildPlatformsAttribute(params BuildPlatform[] platforms)
{
if (platforms == null)
{
throw new ArgumentNullException("platforms");
}
_buildPlatforms = platforms;
}
public override bool EvaluateCondition()
{
var currentPlatform = CurrentPlatform.Current;
if (currentPlatform == default(BuildPlatform))
{
throw new Exception("Unrecognized Platform.");
}
foreach (var platform in _buildPlatforms)
{
if (platform == currentPlatform)
{
return true;
}
}
return false;
}
}
}

View file

@ -0,0 +1,9 @@
using System;
namespace Microsoft.DotNet.Cli.Build.Framework
{
public abstract class TargetConditionAttribute : Attribute
{
public abstract bool EvaluateCondition();
}
}

View file

@ -0,0 +1,9 @@
using System;
namespace Microsoft.DotNet.Cli.Build.Framework
{
public class UndefinedTargetException : Exception
{
public UndefinedTargetException(string message) : base(message) { }
}
}

View file

@ -9,7 +9,7 @@ FROM ubuntu:14.04
# This could become a "microsoft/coreclr" image, since it just installs the dependencies for CoreCLR (and stdlib) # This could become a "microsoft/coreclr" image, since it just installs the dependencies for CoreCLR (and stdlib)
# Install CoreCLR and CoreFx dependencies # Install CoreCLR and CoreFx dependencies
RUN apt-get update && \ RUN apt-get update && \
apt-get -qqy install unzip curl libicu-dev libunwind8 gettext libssl-dev libcurl3-gnutls zlib1g liblttng-ust-dev lldb-3.6-dev lldb-3.6 apt-get -qqy install unzip curl libicu-dev libunwind8 gettext libssl-dev libcurl4-openssl-dev zlib1g liblttng-ust-dev lldb-3.6-dev lldb-3.6
# Install Dotnet CLI dependencies. # Install Dotnet CLI dependencies.
# clang is required for dotnet-compile-native # clang is required for dotnet-compile-native

View file

@ -18,9 +18,18 @@ namespace Microsoft.DotNet.Cli.Build
[Target(nameof(Init), nameof(RestorePackages))] [Target(nameof(Init), nameof(RestorePackages))]
public static BuildTargetResult Prepare(BuildTargetContext c) => c.Success(); public static BuildTargetResult Prepare(BuildTargetContext c) => c.Success();
[Target(nameof(CheckPrereqCmakePresent), nameof(CheckPrereqDebianPackageBuildComponents), nameof(CheckPrereqCoreclrDependencyPackages))] [Target(nameof(CheckPrereqCmakePresent), nameof(CheckPlatformDependencies))]
public static BuildTargetResult CheckPrereqs(BuildTargetContext c) => c.Success(); public static BuildTargetResult CheckPrereqs(BuildTargetContext c) => c.Success();
[Target(nameof(CheckCoreclrPlatformDependencies), nameof(CheckInstallerBuildPlatformDependencies))]
public static BuildTargetResult CheckPlatformDependencies(BuildTargetContext c) => c.Success();
[Target(nameof(CheckUbuntuCoreclrDependencies), nameof(CheckCentOSCoreclrDependencies))]
public static BuildTargetResult CheckCoreclrPlatformDependencies(BuildTargetContext c) => c.Success();
[Target(nameof(CheckUbuntuDebianPackageBuildDependencies))]
public static BuildTargetResult CheckInstallerBuildPlatformDependencies(BuildTargetContext c) => c.Success();
// All major targets will depend on this in order to ensure variables are set up right if they are run independently // All major targets will depend on this in order to ensure variables are set up right if they are run independently
[Target(nameof(GenerateVersions), nameof(CheckPrereqs), nameof(LocateStage0))] [Target(nameof(GenerateVersions), nameof(CheckPrereqs), nameof(LocateStage0))]
public static BuildTargetResult Init(BuildTargetContext c) public static BuildTargetResult Init(BuildTargetContext c)
@ -173,13 +182,9 @@ namespace Microsoft.DotNet.Cli.Build
} }
[Target] [Target]
public static BuildTargetResult CheckPrereqDebianPackageBuildComponents(BuildTargetContext c) [BuildPlatforms(BuildPlatform.Ubuntu)]
public static BuildTargetResult CheckUbuntuDebianPackageBuildDependencies(BuildTargetContext c)
{ {
if (!CurrentPlatform.IsUbuntu)
{
return c.Success();
}
var debianPackageBuildDependencies = new string[] var debianPackageBuildDependencies = new string[]
{ {
"devscripts", "devscripts",
@ -211,58 +216,72 @@ namespace Microsoft.DotNet.Cli.Build
} }
[Target] [Target]
public static BuildTargetResult CheckPrereqCoreclrDependencyPackages(BuildTargetContext c) [BuildPlatforms(BuildPlatform.Ubuntu)]
public static BuildTargetResult CheckUbuntuCoreclrDependencies(BuildTargetContext c)
{ {
if (!CurrentPlatform.IsUbuntu && !CurrentPlatform.IsCentOS) var errorMessageBuilder = new StringBuilder();
var ubuntuCoreclrDependencies = new string[]
{
"unzip",
"curl",
"libicu-dev",
"libunwind8",
"gettext",
"libssl-dev",
"libcurl4-openssl-dev",
"zlib1g",
"liblttng-ust-dev",
"lldb-3.6-dev",
"lldb-3.6"
};
foreach (var package in ubuntuCoreclrDependencies)
{
if (!AptPackageIsInstalled(package))
{
errorMessageBuilder.Append($"Error: Coreclr package dependency {package} missing.");
errorMessageBuilder.Append(Environment.NewLine);
errorMessageBuilder.Append($"-> install with apt-get install {package}");
errorMessageBuilder.Append(Environment.NewLine);
}
}
if (errorMessageBuilder.Length == 0)
{ {
return c.Success(); return c.Success();
} }
else
{
return c.Failed(errorMessageBuilder.ToString());
}
}
[Target]
[BuildPlatforms(BuildPlatform.CentOS)]
public static BuildTargetResult CheckCentOSCoreclrDependencies(BuildTargetContext c)
{
var errorMessageBuilder = new StringBuilder(); var errorMessageBuilder = new StringBuilder();
var platformPackageCheckAction = default(Func<string, bool>);
var platformCoreclrDependencies = default(string[]);
if (CurrentPlatform.IsUbuntu) var centOSCoreclrDependencies = new string[]
{ {
platformCoreclrDependencies = new string[] "unzip",
{ "libunwind",
"unzip", "gettext",
"curl", "libcurl-devel",
"libicu-dev", "openssl-devel",
"libunwind8", "zlib",
"gettext", "libicu-devel"
"libssl-dev", };
"libcurl3-gnutls",
"zlib1g",
"liblttng-ust-dev",
"lldb-3.6-dev",
"lldb-3.6"
};
platformPackageCheckAction = AptPackageIsInstalled; foreach (var package in centOSCoreclrDependencies)
}
else if (CurrentPlatform.IsCentOS)
{ {
platformCoreclrDependencies = new string[] if (!YumPackageIsInstalled(package))
{
"unzip",
"libunwind",
"gettext",
"libcurl-devel",
"openssl-devel",
"zlib",
"libicu-devel"
};
platformPackageCheckAction = YumPackageIsInstalled;
}
foreach (var package in platformCoreclrDependencies)
{
if (!platformPackageCheckAction(package))
{ {
errorMessageBuilder.Append($"Error: Coreclr package dependency {package} missing."); errorMessageBuilder.Append($"Error: Coreclr package dependency {package} missing.");
errorMessageBuilder.Append(Environment.NewLine); errorMessageBuilder.Append(Environment.NewLine);
errorMessageBuilder.Append($"-> install with yum install {package}");
errorMessageBuilder.Append(Environment.NewLine);
} }
} }
@ -313,6 +332,7 @@ cmake is required to build the native host 'corehost'";
var result = Command.Create("dpkg", "-s", packageName) var result = Command.Create("dpkg", "-s", packageName)
.CaptureStdOut() .CaptureStdOut()
.CaptureStdErr() .CaptureStdErr()
.QuietBuildReporter()
.Execute(); .Execute();
return result.ExitCode == 0; return result.ExitCode == 0;
@ -323,6 +343,7 @@ cmake is required to build the native host 'corehost'";
var result = Command.Create("yum", "list", "installed", packageName) var result = Command.Create("yum", "list", "installed", packageName)
.CaptureStdOut() .CaptureStdOut()
.CaptureStdErr() .CaptureStdErr()
.QuietBuildReporter()
.Execute(); .Execute();
return result.ExitCode == 0; return result.ExitCode == 0;

View file

@ -4,9 +4,14 @@ namespace Microsoft.DotNet.Cli.Build
{ {
public class Program public class Program
{ {
public static int Main(string[] args) => BuildSetup.Create(".NET Core CLI") public static int Main(string[] args)
.UseStandardGoals() {
.UseAllTargetsFromAssembly<Program>() DebugHelper.HandleDebugSwitch(ref args);
.Run(args);
return BuildSetup.Create(".NET Core CLI")
.UseStandardGoals()
.UseAllTargetsFromAssembly<Program>()
.Run(args);
}
} }
} }