MSBuild WriteCodeFragment任务

6

在此处找到了一个参考 https://github.com/Microsoft/msbuild/blob/c1459f5cbd36f2c33eafc8f4ff087f6ee84c3640/src/XMakeTasks/Microsoft.Common.CurrentVersion.targets#L3030,但不确定它确切的功能是什么。 - stijn
1
@stijn 在我进行了更多的调查后,请查看答案。 - Dustin Oprea
1个回答

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

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;
}

我找到的唯一其他例子是这个:

(使用 WriteCodeFragment MSBuild 任务)

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

4
我会很乐意帮您找出如何防止它执行此任务的方法。 - T.S
@T.S:按照MSBuild 15创建程序集信息文件并无法编译的提示,可以禁用任务。在您的csproj中添加此属性定义: <PropertyGroup> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> </PropertyGroup> - Grumpy Curmudgeon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接