65.9K
CodeProject 正在变化。 阅读更多。
Home

CSBrick:将您的项目作为源文件包含,而不是二进制文件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (6投票s)

2019年12月16日

MIT

4分钟阅读

viewsIcon

16475

downloadIcon

139

轻松地在源级别重用整个项目的源,而不是在二进制级别

CSBrick

引言

.NET 提供了许多很酷的功能,可以帮助您以期望的方式部署应用程序。但不幸的是,静态链接(嵌入依赖程序集代码的功能)并不包含在其中。

CSBrick 是解决此问题的一种变通方法。它的作用是收集特定项目指向的所有源,然后将其合并并最小化到一个“源块”中——一个单一的 C# 文件,您可以轻松地将其包含到您的项目中,而不是引用等效的二进制类库。多余的空格和注释会被剥离,因为这个文件并非供人类阅读——而是供原始文件阅读。全局 using#define 会被移到文件顶部,重复的 using 会被移除。文档注释会得到保留。

这段代码是我 Build Pack 的一部分——一套构建工具和代码,旨在使构建构建工具更容易、更好。构建工具不能携带额外的 DLL,因为这会使它们作为预构建步骤变得复杂,因此将所有代码保留在单个可执行文件中很重要。此工具非常适合这一点。

构建这个大杂烩

构建包使用自身来构建自身,但由于我从提供的 zip 文件中删除了所有二进制文件,因此您需要自行启动。在 Visual Studio 中打开它,并在 **Release** 模式下构建几次,直到文件锁定错误消失。这是 VS 在处理循环构建步骤时出现的卡顿,虽然没问题,但会导致这些消息出现。第二次构建后,您应该就没问题了,但请切换到“输出”窗格以确保构建成功,因为“错误”窗格倾向于“卡住”在此问题上。最后,切换到 Debug 模式,您就可以开始工作了。任何时候需要重新构建,您都必须在 Release 模式下进行重新构建,才能将对构建工具本身的更改反映到下一次构建中。这是因为这些项目使用同一解决方案中其他项目的 Release 二进制文件作为构建步骤来构建自身。

使用这个烂摊子

使用起来非常直接。这是使用界面

CsBrick merges and minifies C# project source files

csbrick.exe <inputfile> [/output <outputfile>] 
[/ifstale] [/exclude [<exclude1> <exclude2> ... <excludeN>]]
   [/linewidth <linewidth>] [/definefiles]

   <inputfile>     The input project file to use.
   <outputfile>    The output file to use - default stdout.
   <ifstale>       Do not generate unless <outputfile> is older than <inputfile> 
                   or its associated files.
   <exclude>       Exclude the specified file(s) from the output. 
                   - defaults to "Properties\AssemblyInfo.cs"
   <linewidth>     After the width is hit, break the line at the next opportunity 
                   - defaults to 150
   <definefiles>   Insert #define decls for every file included

<inputfile> 必须是 .csproj 文件。它适用于 Visual Studio 2017 项目,但也应该适用于其他项目。我只是还没有在其他版本上尝试过。

您可以将其添加为 Visual Studio 中项目的预构建事件。只需转到“项目 | 属性 | 生成事件”,然后添加命令行。我建议使用 $(SolutionDir)$(ProjectDir) 等宏。例如,包含的 Deslang 项目具有此生成事件

$(SolutionDir)CSBrick\bin\Release\csbrick.exe 
$(SolutionDir)CodeDomGoKit\CodeDomGoKit.csproj /output $(ProjectDir)CodeDomGoKit.brick.cs

在这种情况下,它会获取 CodeDomGoKit.csproj 的所有代码,并将其打包成一个单一文件 CodeDomGoKit.brick.cs,保存在其自己的项目目录中,然后将其包含。这基本上相当于在“引用”中添加 CodeDomGoKit,但没有额外的程序集,而这正是关键所在。

不要编辑 brick 文件。修改创建 brick 文件的原始源。由于生成事件,任何时候发生更改,它都会自动重新生成。

编写这个混乱的程序

我之前已经编写过一个早期的版本,但这个版本更适合我的需求。它使用了我的 ParseContext 类,我在 这里 进行了介绍。它实际上并没有解析 C#,而是对 C# 进行基本的标记化,并丢弃多余的标记,如空格和(非文档)注释。唯一真正棘手的地方是知道何时丢弃空格,以及跳过字符串内部的内容。

这一切都在 Minifier.cs 中,主要是 MergeMinifyBody()。大部分代码是一个大型的 switch case,执行类似这样的操作

case '\"':
    isIdentOrNum = false;
    pc.TryReadCSharpString();
    writer.Write(pc.GetCapture());
    ocol += pc.CaptureBuffer.Length;
    break;
case '\'':
    isIdentOrNum = false;
    pc.TryReadCSharpChar();
    writer.Write(pc.GetCapture());
    ocol += pc.CaptureBuffer.Length;
    break;

每次我们设置 isIdentOrNum 时,下一个标记前面都会加上一个空格,否则不会发出任何空格。ocol 跟踪我们的输出列,以便在超过行宽(建议 150)时换行——这可以防止编辑器因单行过长而卡住。

使用 Minifier.cs 很简单,只需调用此方法

void MergeMinify(TextWriter writer, int lineWidth = 0, 
                 bool defineFiles=false,params string[] sourcePaths)

例如

Minifier.MergeMinify(output, linewidth, definefiles,
    inputs.ToArray());

其中 output 是您的输出 TextReaderlinewidth 是所需的行宽(建议 150),definefiles 是一个布尔值,如果为 true,则为每个包含的文件插入 #define,例如 #define FOO_CS 表示“foo.cs”,inputs 是一个 string 文件名数组,用于处理。

最后,您得到的大部分杂乱的代码就是项目中(除了 AssemblyInfo.cs)的所有代码打包到一个文件中。使用它而不是引用项目。这会增加二进制文件的大小,但可以避免您需要携带 DLL。这就是为什么我说它是缺乏静态链接的一种变通方法。

历史

  • 2019 年 12 月 16 日 - 初次提交
CSBrick:将您的项目作为源文件包含,而不是二进制文件 - CodeProject - 代码之家
© . All rights reserved.