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

一次运行所有作业的实用工具

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (11投票s)

2008年5月5日

Zlib

6分钟阅读

viewsIcon

81949

downloadIcon

373

一个命令行工具,用于并行运行多个程序(即并发地,作为一个组)。

加速你的 Visual Studio 项目构建!

你是否经常希望有一个批处理(命令行)命令,可以让你同时启动几个程序,然后等待“所有”程序作为一个组一起完成,然后再继续下一步?

能够编写一个批处理文件,允许你启动几个 VCBUILDs,让它们相互并行运行(即同时运行或“并行”运行),从而充分利用你新购买的双核或四核处理器系统,从而大大缩短产品构建所需的时间,这该有多方便?

好了,有了“RunJobs”,现在你可以做到了!

背景

如果你过去曾经搜索过 Microsoft Visual Studio 支持论坛,你可能会注意到不止一个人询问过并行运行多个构建的可能性(即进行“批处理构建”,但让每个解决方案配置都并行构建,而不是一个接一个地串行构建),而微软总是回答说,由于某些技术上的困难,这是不可能的。

对我来说,这是不可接受的,因为,如果设计(配置)得当,每个解决方案项目的不同构建配置“应该”指定不同的中间目录和输出目录,从而允许它们中的每一个(Debug|Win32、Release|Win32、Debug|x64、Release|x64 等)被“并行”构建,因为它们之间没有相互依赖关系。

我从不是一个满足于任何“做不到!”回应的人,我开始开发一种简单的方法来完成这项任务,从而大大缩短解决方案的构建时间(特别是对于拥有许多项目、每个项目又有许多不同构建配置的大型复杂解决方案)。

使用 RunJobs

RunJobs 是一个简单的 MFC 命令行程序,允许用户同时启动几个不同的程序。它之所以用 MFC 编写,仅仅是因为这是我个人偏好编写 Windows 程序的方式。当然,这并非必需。由于它是一个非常简单的程序,可以很容易地移植到你选择的任何其他语言。

我将首先描述如何使用它,然后简要说明它的工作原理。

在启动程序时,如果不带任何参数或使用标准的 /? 参数,将显示以下“帮助”文本,我希望它能够自明地描述如何使用该工具。

                     RunJobs
        Fish's Run All Jobs at Once utility
                Version 1.1.2.nnn

  Copyright (C), Software Development Laboratories
         http://www.softdevlabs.com/runjobs
                fish@softdevlabs.com

Format:

   RunJobs   <jobsfile>   [ -j nnn ]   [ -rcfile xxxxx ]


Where:

   <jobsfile>      filename of command-lines to be started
   -j nnn          maximum number of simultaneous commands
   -rcfile xxxxx   name of optional return codes file


The jobs file is a simple verbatim list of the command-lines
that you wish to issue.

Lines whose first non-blank character is '#', ';' or '*' are
considered comment lines and are ignored. All blank lines are
also ignored.

All other lines contain the exact command-line to be issued.

The -j parameter sets an upper limit to the maximum number of
simultaneously running commands.

When the jobs file contains more than this number of commands
the next job (command) is not started until one or more of the
previously issued jobs (commands) finishes first.

Specifying 0 (zero) indicates no upper limit (i.e. ALL listed
jobs will be started simultaneously regardless of how ever many
there are).

The default for -j is the value of the "RUNJOBS_DEFAULT_MAXJOBS"
environment variable if defined or the number of CPUs installed
on your system if it is not defined.

The return code from RunJobs is always the maximum returned
return code value from any of the started jobs. If the -rcfile
option is specified the return code value from each job is also
written to the specified return codes file using the following
format: "n = rc" where 'n' is the job number and 'rc' is its
corresponding return code value.


                         USAGE NOTE

Since RunJobs doesn't parse jobs file command-lines, all of your
environment variable strings on your command-line SHOULD have a
valid (non-empty) value assigned to it. Otherwise an improperly
formed command-line will be built since ExpandEnvironmentStrings
leaves the variable unexpanded whenever it cannot find it. For
example: if your specified command-line is:

    VCBUILD  %VCBUILDTYPE%  "MyProj.vcproj"  "Debug|Win32"

then VCBUILD fails (and crashes if invoked within the IDE!) with
the following error:

  "vcbuild.exe : warning VCBLD6002: invalid option Debug|Win32
                 specified. The option was ignored."

To prevent this, ensure that all environment variable strings on
your command-line are expandable.

在示例项目中(实际上是构建 RunJobs 工具本身),vcbuild-all.cmd 批处理文件说明了如何使用 RunJobs 并行构建项目的所有各种配置。

首先,你需要将 RunJobs.exe 复制到 Windows 可以找到的位置(即 Windows 搜索 PATH 中的某个位置)。

然后,只需定义一些额外的解决方案/项目配置,调用 vcbuild-all.cmd 批处理文件(请参阅项目源代码)来运行 RunJobs,并使用正确的作业文件,该作业文件自然会设置为发出实际的 VCBUILD 命令,以构建你希望在该特定配置下并行构建的各种配置。

请参考提供的 Visual Studio 项目(包括 .sln 解决方案文件、.vcproj 项目文件以及 vcbuild-all.cmd 批处理文件和相关的 All-Debug.jobsAll-Release.jobs 等文件),以了解如何实现此目标的实际示例。

RunJobs 内部

CMyCmdLineParser 类解析命令行参数,并将全局变量设置为要运行的“作业”文件名和所需的“最大并发作业”值。

CStdioFileEx 类用于读取指定的“作业”文件,并在 CPipedProcessCtl 对象列表 CList 中创建新条目。CPipedProcessCtl 类是一个简单的自定义类,通过 CreateProcess 轻松启动子进程,并将其 STDIN/STDOUT/STDERR 文件重定向回创建进程。

当每个启动的进程向其 STDOUT/STDERR 文件写入内容时,CPipedProcessCtl 类自动创建的独立管道监控线程会将输出重定向到 RunJob 的实际 STDOUT/STDERR 文件(当然,这会被 Visual Studio 拦截并显示在输出窗格中)。

当每个进程结束时,将检索返回码(通过 GetExitCodeProcess)并保存,其中任何一个启动进程的最高返回码将用作 RunJob 本身的返回码。通过这种方式,可以在批处理文件中检查所有“作业”是否作为一个整体成功完成。

预期构建时间改进

在我的双核 2.4GHz Xeon 开发平台上(超线程后看起来像四核处理器),RunJobs 将一个小型、半复杂的、多项目/多构建配置(Debug|Win32、Release|Win32、Unicode Debug|Win32、Unicode Release|Win32,外加 x64 平台的相同四种配置,即每个项目共八种构建配置)的构建时间从 9:48 缩短到 5:37。改进了约 43%。

rdboss(请参阅下面的评论)写道:

这个产品对我们来说是天赐之物。[...]我们的夜间构建过程从大约 20 小时缩短到大约 10 小时!

然而,Esteban Papp(请参阅下面的评论)写道:

我们正在使用它来进行多平台并行构建,它在一台 2xQuad-Core 3Ghz 的机器上运行良好,我们的构建时间以前是 1 小时,现在是 7 分钟。

这比原来快了‘8.5 倍’!‘将近 10 倍’!哇!

当然,我应该提醒,“您的实际效果可能有所不同”。这完全取决于您的项目/解决方案的复杂性(每个项目有多少源文件、每个项目有多少不同的配置等),但关键在于,能够并行构建多个项目配置确实有帮助!(这当然也是我最初编写 RunJobs 的原因)

总的来说,根据上述报告,我预计您的构建时间将会有 50% 到 150% 或更高的改进。

对于这样一个简单的命令行工具来说,这可不算差,对吧? :)

闭幕词

好了,我想就这些了。真的没有太多要补充的了。希望大家都能喜欢使用 RunJobs 来加快构建速度,并期待听到它对你们有多大的帮助。

就是这样! 使用 RunJobs 享受更快的构建时间! :)

历史

  • 2008 年 5 月:发布 1.0.0 版本并撰写文章

  • 2008 年 11 月:版本 1.0.2
    • 修正了 esteban.papp 发现的返回码问题
    • 转换为 Visual C++ 2008 SP1
    • 静态链接到 MFC 和 CRT

  • 2009 年 3 月:版本 1.1.0
    • 修复了取消传播问题。
    • 添加了 -rcfile 选项
    • 重新设计了项目实现

  • 2009 年 8 月:版本 1.1.2
    • 修正了“StartChildProcess”失败导致总体返回码未更新的问题。
    • 修正了 rdboss 发现的作业编号日志前缀不唯一的问题。(参见评论)

© . All rights reserved.