MSBuild WriteCodeFragment 任务

MSBuild WriteCodeFragment Task

我不清楚 WriteCodeFragment 任务的目的是什么 (https://msdn.microsoft.com/en-us/library/microsoft.build.tasks.writecodefragment.aspx)。 Google 似乎只是打开了文档,而我看到的一个例子并不确定。

有人可以澄清一下吗?谢谢

看起来它只适用于用属性填充源代码文件(您选择的语言)(阅读:将构建信息嵌入到项目中)。

这是任务的源代码:

https://github.com/Microsoft/msbuild/blob/master/src/XMakeTasks/WriteCodeFragment.cs

具体的GenerateCode()方法供参考。它主要枚举的类型看起来是特定于属性的(阅读:声明性和非功能性):

/// <summary>
/// Generates the code into a string.
/// If it fails, logs an error and returns null.
/// If no meaningful code is generated, returns empty string.
/// Returns the default language extension as an out parameter.
/// </summary>
[SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.IO.StringWriter.#ctor(System.Text.StringBuilder)", Justification = "Reads fine to me")]
private string GenerateCode(out string extension)
{
    extension = null;
    bool haveGeneratedContent = false;

    CodeDomProvider provider;

    try
    {
        provider = CodeDomProvider.CreateProvider(Language);
    }
    catch (ConfigurationException ex)
    {
        Log.LogErrorWithCodeFromResources("WriteCodeFragment.CouldNotCreateProvider", Language, ex.Message);
        return null;
    }
    catch (SecurityException ex)
    {
        Log.LogErrorWithCodeFromResources("WriteCodeFragment.CouldNotCreateProvider", Language, ex.Message);
        return null;
    }

    extension = provider.FileExtension;

    CodeCompileUnit unit = new CodeCompileUnit();

    CodeNamespace globalNamespace = new CodeNamespace();
    unit.Namespaces.Add(globalNamespace);

    // Declare authorship. Unfortunately CodeDOM puts this comment after the attributes.
    string comment = ResourceUtilities.FormatResourceString("WriteCodeFragment.Comment");
    globalNamespace.Comments.Add(new CodeCommentStatement(comment));

    if (AssemblyAttributes == null)
    {
        return String.Empty;
    }

    // For convenience, bring in the namespaces, where many assembly attributes lie
    globalNamespace.Imports.Add(new CodeNamespaceImport("System"));
    globalNamespace.Imports.Add(new CodeNamespaceImport("System.Reflection"));

    foreach (ITaskItem attributeItem in AssemblyAttributes)
    {
        CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(new CodeTypeReference(attributeItem.ItemSpec));

        // Some attributes only allow positional constructor arguments, or the user may just prefer them.
        // To set those, use metadata names like "_Parameter1", "_Parameter2" etc.
        // If a parameter index is skipped, it's an error.
        IDictionary customMetadata = attributeItem.CloneCustomMetadata();

        List<CodeAttributeArgument> orderedParameters = new List<CodeAttributeArgument>(new CodeAttributeArgument[customMetadata.Count + 1] /* max possible slots needed */);
        List<CodeAttributeArgument> namedParameters = new List<CodeAttributeArgument>();

        foreach (DictionaryEntry entry in customMetadata)
        {
            string name = (string)entry.Key;
            string value = (string)entry.Value;

            if (name.StartsWith("_Parameter", StringComparison.OrdinalIgnoreCase))
            {
                int index;

                if (!Int32.TryParse(name.Substring("_Parameter".Length), out index))
                {
                    Log.LogErrorWithCodeFromResources("General.InvalidValue", name, "WriteCodeFragment");
                    return null;
                }

                if (index > orderedParameters.Count || index < 1)
                {
                    Log.LogErrorWithCodeFromResources("WriteCodeFragment.SkippedNumberedParameter", index);
                    return null;
                }

                // "_Parameter01" and "_Parameter1" would overwrite each other
                orderedParameters[index - 1] = new CodeAttributeArgument(String.Empty, new CodePrimitiveExpression(value));
            }
            else
            {
                namedParameters.Add(new CodeAttributeArgument(name, new CodePrimitiveExpression(value)));
            }
        }

        bool encounteredNull = false;
        for (int i = 0; i < orderedParameters.Count; i++)
        {
            if (orderedParameters[i] == null)
            {
                // All subsequent args should be null, else a slot was missed
                encounteredNull = true;
                continue;
            }

            if (encounteredNull)
            {
                Log.LogErrorWithCodeFromResources("WriteCodeFragment.SkippedNumberedParameter", i + 1 /* back to 1 based */);
                return null;
            }

            attribute.Arguments.Add(orderedParameters[i]);
        }

        foreach (CodeAttributeArgument namedParameter in namedParameters)
        {
            attribute.Arguments.Add(namedParameter);
        }

        unit.AssemblyCustomAttributes.Add(attribute);
        haveGeneratedContent = true;
    }

    StringBuilder generatedCode = new StringBuilder();

    using (StringWriter writer = new StringWriter(generatedCode, CultureInfo.CurrentCulture))
    {
        provider.GenerateCodeFromCompileUnit(unit, writer, new CodeGeneratorOptions());
    }

    string code = generatedCode.ToString();

    // If we just generated infrastructure, don't bother returning anything
    // as there's no point writing the file
    return haveGeneratedContent ? code : String.Empty;
}

我发现的唯一另一个例子是:

(Using WriteCodeFragment MSBuild Task)

<Target Name="BeforeBuild">
  <ItemGroup>
     <AssemblyAttributes Include="AssemblyVersion">
       <_Parameter1>123.132.123.123</_Parameter1>
     </AssemblyAttributes>
  </ItemGroup>
  <WriteCodeFragment Language="C#" OutputFile="BuildVersion.cs" AssemblyAttributes="@(AssemblyAttributes)" />
</Target>

将其插入实际构建渲染中:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Reflection;

[assembly: AssemblyVersion("123.132.123.123")]

// Generated by the MSBuild WriteCodeFragment class.