.netmsbuildmsbuild-task

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


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


Solution

  • 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.