实现 microsoft.build.utilities.task 时,如何访问构建的各种环境变量?

发布于 2024-09-05 16:40:41 字数 428 浏览 11 评论 0 原文

实现 microsoft.build.utilities.task 时,如何访问构建的各种环境变量?

例如“TargetPath”,

我知道我可以将它作为任务 XML 的一部分传递,

<MyTask TargetPath="$(TargetPath)" />

但如果我可以访问代码中的变量,我不想强​​迫任务的使用者必须这样做。

http://msdn.microsoft.com/en-我们/library/microsoft.build.utilities.task.aspx

When implementing a microsoft.build.utilities.task how to i get access to the various environmental variables of the build?

For example "TargetPath"

I know i can pass it in as part of the task XML

<MyTask TargetPath="$(TargetPath)" />

But i don't want to force the consumer of the task to have to do that if I can access the variable in code.

http://msdn.microsoft.com/en-us/library/microsoft.build.utilities.task.aspx

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

皓月长歌 2024-09-12 16:40:41

我想出了如何做到这一点

public static class BuildEngineExtensions
{
    const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public;

    public static IEnumerable GetEnvironmentVariable(this IBuildEngine buildEngine, string key,bool throwIfNotFound)
    {
        var projectInstance = GetProjectInstance(buildEngine);

        var items = projectInstance.Items
            .Where(x => string.Equals(x.ItemType, key, StringComparison.InvariantCultureIgnoreCase)).ToList();
        if (items.Count > 0)
        {
            return items.Select(x => x.EvaluatedInclude);
        }


        var properties = projectInstance.Properties
            .Where(x => string.Equals(x.Name, key, StringComparison.InvariantCultureIgnoreCase)).ToList();
        if (properties.Count > 0)
        {
            return properties.Select(x => x.EvaluatedValue);
        }

        if (throwIfNotFound)
        {
            throw new Exception(string.Format("Could not extract from '{0}' environmental variables.", key));
        }

        return Enumerable.Empty();
    }

    static ProjectInstance GetProjectInstance(IBuildEngine buildEngine)
    {
        var buildEngineType = buildEngine.GetType();
        var targetBuilderCallbackField = buildEngineType.GetField("targetBuilderCallback", bindingFlags);
        if (targetBuilderCallbackField == null)
        {
            throw new Exception("Could not extract targetBuilderCallback from " + buildEngineType.FullName);
        }
        var targetBuilderCallback = targetBuilderCallbackField.GetValue(buildEngine);
        var targetCallbackType = targetBuilderCallback.GetType();
        var projectInstanceField = targetCallbackType.GetField("projectInstance", bindingFlags);
        if (projectInstanceField == null)
        {
            throw new Exception("Could not extract projectInstance from " + targetCallbackType.FullName);
        }
        return (ProjectInstance)projectInstanceField.GetValue(targetBuilderCallback);
    }
}

它可以像这样使用是的,

string targetPath = buildEngine.GetEnvironmentVariable("TargetPath", true).First();
string intermediateAssembly = buildEngine.GetEnvironmentVariable("IntermediateAssembly", true).First();
IEnumerable<string> referencePaths = buildEngine.GetEnvironmentVariable("ReferencePath", true);

这是丑陋的黑魔法,但它有效。

I worked out how to do this

public static class BuildEngineExtensions
{
    const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public;

    public static IEnumerable GetEnvironmentVariable(this IBuildEngine buildEngine, string key,bool throwIfNotFound)
    {
        var projectInstance = GetProjectInstance(buildEngine);

        var items = projectInstance.Items
            .Where(x => string.Equals(x.ItemType, key, StringComparison.InvariantCultureIgnoreCase)).ToList();
        if (items.Count > 0)
        {
            return items.Select(x => x.EvaluatedInclude);
        }


        var properties = projectInstance.Properties
            .Where(x => string.Equals(x.Name, key, StringComparison.InvariantCultureIgnoreCase)).ToList();
        if (properties.Count > 0)
        {
            return properties.Select(x => x.EvaluatedValue);
        }

        if (throwIfNotFound)
        {
            throw new Exception(string.Format("Could not extract from '{0}' environmental variables.", key));
        }

        return Enumerable.Empty();
    }

    static ProjectInstance GetProjectInstance(IBuildEngine buildEngine)
    {
        var buildEngineType = buildEngine.GetType();
        var targetBuilderCallbackField = buildEngineType.GetField("targetBuilderCallback", bindingFlags);
        if (targetBuilderCallbackField == null)
        {
            throw new Exception("Could not extract targetBuilderCallback from " + buildEngineType.FullName);
        }
        var targetBuilderCallback = targetBuilderCallbackField.GetValue(buildEngine);
        var targetCallbackType = targetBuilderCallback.GetType();
        var projectInstanceField = targetCallbackType.GetField("projectInstance", bindingFlags);
        if (projectInstanceField == null)
        {
            throw new Exception("Could not extract projectInstance from " + targetCallbackType.FullName);
        }
        return (ProjectInstance)projectInstanceField.GetValue(targetBuilderCallback);
    }
}

And it can be used like this

string targetPath = buildEngine.GetEnvironmentVariable("TargetPath", true).First();
string intermediateAssembly = buildEngine.GetEnvironmentVariable("IntermediateAssembly", true).First();
IEnumerable<string> referencePaths = buildEngine.GetEnvironmentVariable("ReferencePath", true);

Yes it is ugly and black magic but it works.

只是一片海 2024-09-12 16:40:41

你不能轻易做到这一点,也不应该这样做。任务不应该知道其执行上下文,并且应该使用其输入参数。

免责声明:不要这样做!

如果你真的想这样做,你需要用类似的东西重新解析项目文件。

public override bool Execute()
{
  string projectFile = BuildEngine.ProjectFileOfTaskNode;

  Engine buildEngine = new Engine(System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory());

  Project project = new Project(buildEngine);
  project.Load(projectFile);
  foreach(var o in project.EvaluatedProperties)
  {
    // Use properties
  }

  // Do what you want

  return true;
}

You can not do this easily and you shouldn't do it. A task shouldn't know its context of execution, and should work with its input parameters.

Disclaimer : Don't do it!

If you really want to do it, you would need to reparse the project file with something like that.

public override bool Execute()
{
  string projectFile = BuildEngine.ProjectFileOfTaskNode;

  Engine buildEngine = new Engine(System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory());

  Project project = new Project(buildEngine);
  project.Load(projectFile);
  foreach(var o in project.EvaluatedProperties)
  {
    // Use properties
  }

  // Do what you want

  return true;
}
眼泪淡了忧伤 2024-09-12 16:40:41

基于 Simon 的答案,该答案

  • 在 2024 年不会立即起作用,因为私有字段的名称现在以 为前缀_
  • 用作 扩展方法

我最终得到了以下结果。请注意,ProjectInstance 类型被视为“特定于实现”,因此您必须将 PackageReference 添加到 Microsoft.Build 才能访问它。

public static class BuildEngineExtensions
{
    private const BindingFlags BindingFlags = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public;

    public static ProjectInstance GetProjectInstance(this IBuildEngine buildEngine)
    {
        var buildEngineType = buildEngine.GetType();
        var targetBuilderCallbackField = buildEngineType.GetField("_targetBuilderCallback", BindingFlags);
        if (targetBuilderCallbackField is null)
            throw new InvalidOperationException($"Could not extract _targetBuilderCallback from {buildEngineType.FullName}");

        var targetBuilderCallback = targetBuilderCallbackField.GetValue(buildEngine);
        var targetCallbackType = targetBuilderCallback!.GetType();
        var projectInstanceField = targetCallbackType.GetField("_projectInstance", BindingFlags);

        if (projectInstanceField is null)
            throw new InvalidOperationException($"Could not extract _projectInstance from {targetCallbackType.FullName}");

        if (projectInstanceField.GetValue(targetBuilderCallback) is not ProjectInstance project)
            throw new InvalidOperationException();

        return project;
    }
}

我发现 ProjectInstance 包含 GetPropertyGetItems 的公共定义,因此我没有费心使用反射来访问这些定义。

为了方便起见,并减轻通过反射获取 ProjectInstance 的开销,我定义了一个 TaskBase 类:

public abstract class TaskBase : Microsoft.Build.Utilities.Task
{
    private ProjectInstance? _project;
    protected ProjectInstance Project => _project ??= BuildEngine.GetProjectInstance();

    protected string ProjectName => Project.GetPropertyValue("ProjectName");
}

我发现它工作得非常好!

public class MyTask : TaskBase
{
    public void Execute() 
    {
        Log.LogMessage(MessageImportance.High, $"This task was called by {ProjectName}!");
        return true;
    }
}

Building upon Simon's answer, which

  • Doesn't immediately work in 2024 as the private fields' names are now prefixed with _
  • Would be much more convenient to use as an extension method

I ended up with the following. Note, the ProjectInstance type is considered 'implementation specific' and so you have to add a PackageReference to Microsoft.Build to access it.

public static class BuildEngineExtensions
{
    private const BindingFlags BindingFlags = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public;

    public static ProjectInstance GetProjectInstance(this IBuildEngine buildEngine)
    {
        var buildEngineType = buildEngine.GetType();
        var targetBuilderCallbackField = buildEngineType.GetField("_targetBuilderCallback", BindingFlags);
        if (targetBuilderCallbackField is null)
            throw new InvalidOperationException(
quot;Could not extract _targetBuilderCallback from {buildEngineType.FullName}");

        var targetBuilderCallback = targetBuilderCallbackField.GetValue(buildEngine);
        var targetCallbackType = targetBuilderCallback!.GetType();
        var projectInstanceField = targetCallbackType.GetField("_projectInstance", BindingFlags);

        if (projectInstanceField is null)
            throw new InvalidOperationException(
quot;Could not extract _projectInstance from {targetCallbackType.FullName}");

        if (projectInstanceField.GetValue(targetBuilderCallback) is not ProjectInstance project)
            throw new InvalidOperationException();

        return project;
    }
}

I found that ProjectInstance contains public definitions for GetProperty and GetItems so I didn't bother using reflection to access those.

For convenience, and to mitigate the overhead of getting the ProjectInstance by reflection, I defined a TaskBase class:

public abstract class TaskBase : Microsoft.Build.Utilities.Task
{
    private ProjectInstance? _project;
    protected ProjectInstance Project => _project ??= BuildEngine.GetProjectInstance();

    protected string ProjectName => Project.GetPropertyValue("ProjectName");
}

Which I found worked perfectly!

public class MyTask : TaskBase
{
    public void Execute() 
    {
        Log.LogMessage(MessageImportance.High, 
quot;This task was called by {ProjectName}!");
        return true;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文