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;
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
@ -53,7 +59,6 @@ namespace Microsoft.DotNet.Cli.Build.Framework
return result;
}
// It hasn't, or we're forcing, so run it
result = ExecTarget(target);
_completedTargets[target.Name] = result;
@ -80,6 +85,29 @@ namespace Microsoft.DotNet.Cli.Build.Framework
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)
{
if (target == null)

View file

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

View file

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

View file

@ -25,6 +25,7 @@ namespace Microsoft.DotNet.Cli.Build.Framework
private Action<string> _stdErrHandler;
private bool _running = false;
private bool _quietBuildReporter = false;
private Command(string executable, string args)
{
@ -148,6 +149,12 @@ namespace Microsoft.DotNet.Cli.Build.Framework
return this;
}
public Command QuietBuildReporter()
{
_quietBuildReporter = true;
return this;
}
public CommandResult Execute()
{
ThrowIfRunning();
@ -172,7 +179,7 @@ namespace Microsoft.DotNet.Cli.Build.Framework
_process.EnableRaisingEvents = true;
var sw = Stopwatch.StartNew();
BuildReporter.BeginSection("EXEC", FormatProcessInfo(_process.StartInfo));
ReportExecBegin();
_process.Start();
@ -190,15 +197,7 @@ namespace Microsoft.DotNet.Cli.Build.Framework
var exitCode = _process.ExitCode;
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);
}
ReportExecEnd(exitCode);
return new CommandResult(
_process.StartInfo,
@ -299,6 +298,30 @@ namespace Microsoft.DotNet.Cli.Build.Framework
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)
{
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 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
{
get
public static bool IsWindows
{
return RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
get
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
}
}
}
public static bool IsLinux
{
get
public static bool IsOSX
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
get
{
return RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
}
}
}
public static bool IsUbuntu
{
get
public static bool IsUbuntu
{
var osname = PlatformServices.Default.Runtime.OperatingSystem;
return string.Equals(osname, "ubuntu", StringComparison.OrdinalIgnoreCase);
get
{
var osname = PlatformServices.Default.Runtime.OperatingSystem;
return string.Equals(osname, "ubuntu", StringComparison.OrdinalIgnoreCase);
}
}
}
public static bool IsCentOS
{
get
public static bool IsCentOS
{
var osname = PlatformServices.Default.Runtime.OperatingSystem;
return string.Equals(osname, "centos", StringComparison.OrdinalIgnoreCase);
get
{
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
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class TargetAttribute : Attribute
{
public class TargetAttribute : Attribute
{
public string Name { get; set; }
public IEnumerable<string> Dependencies { get; }
@ -20,5 +20,5 @@ namespace Microsoft.DotNet.Cli.Build.Framework
{
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)
# Install CoreCLR and CoreFx dependencies
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.
# clang is required for dotnet-compile-native

View file

@ -18,9 +18,18 @@ namespace Microsoft.DotNet.Cli.Build
[Target(nameof(Init), nameof(RestorePackages))]
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();
[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
[Target(nameof(GenerateVersions), nameof(CheckPrereqs), nameof(LocateStage0))]
public static BuildTargetResult Init(BuildTargetContext c)
@ -173,13 +182,9 @@ namespace Microsoft.DotNet.Cli.Build
}
[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[]
{
"devscripts",
@ -211,58 +216,72 @@ namespace Microsoft.DotNet.Cli.Build
}
[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();
}
else
{
return c.Failed(errorMessageBuilder.ToString());
}
}
[Target]
[BuildPlatforms(BuildPlatform.CentOS)]
public static BuildTargetResult CheckCentOSCoreclrDependencies(BuildTargetContext c)
{
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",
"curl",
"libicu-dev",
"libunwind8",
"gettext",
"libssl-dev",
"libcurl3-gnutls",
"zlib1g",
"liblttng-ust-dev",
"lldb-3.6-dev",
"lldb-3.6"
};
"unzip",
"libunwind",
"gettext",
"libcurl-devel",
"openssl-devel",
"zlib",
"libicu-devel"
};
platformPackageCheckAction = AptPackageIsInstalled;
}
else if (CurrentPlatform.IsCentOS)
foreach (var package in centOSCoreclrDependencies)
{
platformCoreclrDependencies = new string[]
{
"unzip",
"libunwind",
"gettext",
"libcurl-devel",
"openssl-devel",
"zlib",
"libicu-devel"
};
platformPackageCheckAction = YumPackageIsInstalled;
}
foreach (var package in platformCoreclrDependencies)
{
if (!platformPackageCheckAction(package))
if (!YumPackageIsInstalled(package))
{
errorMessageBuilder.Append($"Error: Coreclr package dependency {package} missing.");
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)
.CaptureStdOut()
.CaptureStdErr()
.QuietBuildReporter()
.Execute();
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)
.CaptureStdOut()
.CaptureStdErr()
.QuietBuildReporter()
.Execute();
return result.ExitCode == 0;

View file

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