开发区域

MATLAB的高级软件开发

构建块

我的人!哦,我是多么想念你。我们已经很长时间没有在博客上讨论开发人员工作流的优点了。我发现很难坐下来写下更多关于这些主题的想法和思考,但这里的一线希望是我缺乏时间的一个重要原因,因为我们一直在努力交付MATLAB的开发基础设施。

其中之一就是R2022b中包含的新的MATLAB构建工具!我们对这个工具的新手发布感到非常兴奋,但更令人兴奋的是,当您开始将它用于您的MATLAB项目时,它将带来所有的价值。

这到底是什么东西?简而言之,它是一个标准接口,供您在MATLAB项目中构建和协作。你说“建造?”

是的,“建造!”我说。任何开发严肃的、可共享的、产品级MATLAB代码的人都知道,尽管MATLAB是一种“易于利用”的语言,通常不需要实际的“编译”步骤,但它仍然需要一个包括测试、质量检验关和更改发布号等任务的开发过程。事实证明,MATLAB在很多方面确实做到了构建某物想想mex文件,p-code,代码生成,工具箱包,文档页,或者从MATLAB编译器和编译器SDK生成工件。这些都是构建步骤。

但问题是,目前还没有针对MATLAB项目的标准API来组织这些构建步骤。它通常看起来像这样:

这看起来眼熟吗?对我来说是这样的。所有这些脚本都是在执行这些特定任务的项目或回购中增长的。每一封看起来都有些不同,因为一封是在周二写的,而另一封是在下周一写的。如果幸运的话,当我们需要与它们互动时,我们会记得它们是如何工作的。然而,有时我们并不幸运。有时我们回到我们的代码中,完全不知道我们是如何构建它的,以什么顺序,使用什么脚本。

另外,要知道谁是从来没有那么幸运?一个新的贡献者。有人想为您的代码做出贡献,但不了解您为开发项目而设置的系统。有些项目是严格的,并且确实有他们自己定制的构建框架。这对他们来说很好,但需要更多的维护,即使在这些情况下,项目的新开发人员也需要学习这个自定义系统,它与构建MATLAB代码的所有其他系统都不同。

现在不是了。从R2022b我们现在有一个标准的接口和构建框架这使得项目所有者能够以任何人都可以使用的方式轻松地生成他们的构建,无论构建管道有多复杂。我们现在可以从特别脚本和自定义构建系统转移到已知的、结构化的和标准的框架。

让我们以我最喜欢的简单的质量-弹簧-阻尼器的例子(看起来我内心还是一个机械工程师)。这是一个简单的“工具箱”示例,有3个组件,一个设计脚本springMassDamperDesign.m它定义了系统的刚度和阻尼常数,一个函数simulateSystem.m它从一个非平衡的初始条件来模拟系统,以显示阶跃响应和一个mex文件convec.c它将两个数组进行卷积,这对于像这样的动态系统可能是一个有用的工具。它还有几个测试,以确保在代码更改时一切正常。

希望这段代码的作者了解所有这些组件以及为什么要这样编写它们。然而,如果我是这个代码库的第一次贡献者,我就不知道了。我的工作流程可能看起来像这样:

  1. 获取代码
  2. 使用工具箱
  3. 看到有什么我想改变的工具箱,一个功能添加或微调的设计
  4. 做出改变
  5. 提交更改即可获胜!!

看来我要为自己的事业做出坚实的贡献了,我为自己感到骄傲。得到代码后,我看到初始设计看起来是这样的:

函数设计= springMassDamperDesign(质量)如果纳金m =质量;其他的M = 1500;%需要知道质量来确定临界阻尼结束设计。K = 5e6;弹簧常数Design.c = 5e5;%阻尼系数

...当使用所包含的函数进行模拟时:

函数[x, t] = simulatsystem(设计)如果~isstruct(设计)|| ~所有(isfield(设计,{“c”“k”}))错误(“simulateSystem: InvalidDesign: ShouldBeStruct”...“设计应该是一个包含字段c和k的结构”);结束%设计变量C = design.c;K = design.k;%常量变量Z0 = [-0.1;0);初始位置和速度M = 1500;%的质量Odefun = @(t,z) [0 1;- k / m - c / m] * z;[t, z] = ode45(odefun, [0,1], z0);第一列是位置(偏离平衡的位移)X = z(:, 1);

...它产生以下响应:

[t,y] = simulatsystem (springMassDamperDesign);情节(y, t)

相当不错,但我认为还有改进的空间。我认为我们可以更快地回到平衡状态,我喜欢稍微过冲的平滑换挡。因为我是一个优秀的机械工程师,这显然是一个更好的设计,我们只需要减少一点阻尼:

目录.changes / round1
函数设计= springMassDamperDesign(质量)如果纳金m =质量;其他的M = 1500;%需要知道质量来确定临界阻尼结束设计。K = 5e6;弹簧常数Design.c = 1e5;%阻尼系数
[t,y] = simulatsystem (springMassDamperDesign);情节(y, t)

...并提交。这时我得到了一剂谦卑,体验了一个痛苦的世界。工具箱维护人员拒绝了我的提交,因为该设计未能通过一个已经设置的测试,该测试旨在限制响应的超调。看到了吗?

runtests (“测试/ designTest.m”
运行designTest。================================================================================ 验证失败designTest / testOvershoot。---------------- 测试诊断 : ---------------- 过度侵犯!最大超调是0.01  --------------------- 框架的诊断 : --------------------- verifyLessThan失败了。——>该值必须小于最大值。实际值:0.010846982843858最大值(Exclusive): 0.010000000000000 ------------------堆栈信息:------------------ In /Users/acampbel/Library/CloudStorage/OneDrive-MathWorks/repos/msd_blog1/tests/designTest. aspl . aspl . aspl。在23米(testOvershoot)  ================================================================================ ..完成designTest  __________ 失败失败总结:名字不完整的原因(s ) ======================================================================= designTest / testOvershoot X验证失败。ans = 1×3 TestResult数组,属性:名称通过失败不完整持续时间细节合计:2通过,1失败,0不完整0.052733秒测试时间。

回想起来,这很容易预测,毕竟有测试。我应该在提交之前运行一下的。但是没有任何东西给我指明他们的方向,我错过了。对于一个简单的回购例子来说,这似乎是显而易见的,但对于一个“真正的”工具箱来说,这可能很难看到。

好吧,很明显,在我的贡献被一个过度劳累的工具箱作者简洁地拒绝之后,还有更多的工作要做。但我还是能胜任这个任务。在了解到有一个超调要求后,我可以调整我的设计以适应这些限制:

目录.changes / round2
函数设计= springMassDamperDesign(质量)如果纳金m =质量;其他的M = 1500;%需要知道质量来确定临界阻尼结束设计。K = 5e6;弹簧常数Design.c = 1.1e5;%阻尼系数
[t,y] = simulatsystem (springMassDamperDesign);情节(y, t)

看起来不错,通过测试了吗?

runtests (“测试/ designTest.m”
运行designTest…完成designTest __________ ans = 1×3 TestResult数组,属性:名称通过失败不完整持续时间细节合计:3通过,0失败,0不完整。0.011899秒测试时间。

是的!最后我必须完成。然而,当我提交这段代码时,我得到了另一个拒绝,因为仍然有一个测试失败的mex文件实用程序,我甚至不知道:

runtests (“测试”
运行的对流传热  ================================================================================ 错误发生在对流传热/ MatchesConvBaseline和它不运行完成。--------- 错误ID : --------- ' MATLAB: UndefinedFunction ' -------------- 错误的细节 : -------------- 未定义的函数“convec”“双”类型的输入参数。断言(isequal(convecTest(第6行),conv(x,y)),…================================================================================ .完成convecTest __________运行designTest…完成designTest  __________ 失败失败总结:名字不完整的原因(s ) =============================================================== 对流传热/ MatchesConvBaseline X X错误。ans = 1×4 TestResult数组,属性:名称通过失败不完整持续时间细节合计:3通过,1失败,1不完整。0.036491秒测试时间。

好了,在这一点上,我看到有一些工具,我没有改变,使用,甚至不熟悉,它的测试失败。此外,我意识到它失败是因为它没有编译。我不知道如何编译这个mex文件,在这一点上我放弃了,因为我没有计划在这个贡献中投入这么多时间。我没有时间去学习这个回购的所有细节(我只是想调整阻尼系数!)放弃后,我带着不好的味道离开了。我可能已经不再尝试为这个代码库做贡献了,实际上,在尝试为其他代码库做贡献之前,我甚至可能会再三考虑。不好的。没有尔巴布埃纳岛。希望自己的直觉。

进入buildtool

所有这些问题都可以通过使用这个新的构建工具解决。作为一个新的贡献者,我所需要知道的就是我需要调用构建工具来完成作者预期的开发工作流。我需要在第一次学习这一点,但是一旦我熟悉了这个标准框架,我就可以与之交互了任何其他项目这也是在使用构建工具。一旦我看到项目的根目录有一个名为buildfile.m我知道我是在做生意,我可以做作者想要做的任何事情,包括运行测试和编译mex文件,只需调用该工具。让我们试试:

buildtool
**用“Xcode with Clang”启动mex Building。MEX成功完成。**已完成mex **正在启动安装**已完成安装**正在启动测试运行convecTest。完成convecTest __________运行designTest…完成designTest __________ 1×4 TestResult数组,属性:名称通过失败不完整持续时间细节合计:4通过,0失败,0不完整。0.16678秒测试时间。**完成测试

那不是很美吗?我不需要知道项目是如何构建的,我可以很快开始工作。我可以做我的小改动,所有需要发生的事情(例如构建一个mex文件)都会发生,然后我们可以确认它没有通过测试。它使高质量的烘焙更容易。

是怎么做到的?

我一直在关注不熟悉的贡献者的视角。作者/所有者如何利用这一点来获得成功?这非常简单,利用了一个易于使用的可脚本化MATLAB接口作为基本框架。你从创建你的buildfile.m,这是一个创建构建计划的函数。

函数计划= buildfile计划= buildplan(localfunctions);结束

在创建构建计划时传递所有的局部函数,可以很容易地定义简单的任务。这使您能够从任何以单词结尾的函数创建任务任务(或任务_task任务等等)。函数中的第一个注释(H1行)给出了任务描述。在这种情况下,我们有3个任务想要添加。

设置任务

函数setupTask(上下文)构建的安装路径目录(fullfile (context.Plan.RootFolder,“工具箱”));结束

此任务确保为构建准备了正确的路径。你可能会问这是否应该使用MATLAB项目来完成,答案是肯定的!这是一个更好的方法。现在我们正在构建这个,但将在以后的帖子中投射它。

一个mex任务

函数mexTask (~)编译mex文件墨西哥人墨西哥人/ convec.c-outdir工具箱/结束

在本例中,这是一个相当简单的编译,但对于许多项目来说,这一步可能更复杂。无论简单还是复杂,你都可以让它对新手来说微不足道。

测试任务

函数testTask (~)%运行单元测试结果=运行测试(“测试”);disp(结果);assertSuccess(结果);结束

简单。既然已经定义了这些任务并自动包含在构建文件中,任何人都可以看到可以运行哪些任务:

buildtool任务
编译mex文件setup -构建测试的安装路径-运行单元测试

很好,我们可以看到我们的3个任务,但正如您可能预测的那样,这些任务不能以任何顺序运行。除非路径上有适当的代码并构建了mex文件,否则测试无法通过。这些任务依赖关系可以在设置计划时在主函数中定义。我们需要添加这些依赖项,当我们这样做时,让我们设置一个默认任务以便buildtool将工作,甚至不传递任何参数。

函数计划= buildfile计划= buildplan(localfunctions);计划(“测试”).依赖关系= [“墨西哥人”“设置”];计划。DefaultTasks =“测试”结束

现在我们可以通过调用来默认调用它buildtool(就像我们上面做的那样)或者我们可以调用一个我们想要运行的特定任务,比如mex,它只会运行该任务所需的内容:

buildtool墨西哥人
**用“Xcode with Clang”启动mex Building。MEX成功完成。**已完成mex

以下是完整的构建文件供您参考:

函数计划= buildfile计划= buildplan(localfunctions);计划(“测试”).依赖关系= [“墨西哥人”“设置”];计划。DefaultTasks =“测试”结束函数setupTask(上下文)构建的安装路径目录(fullfile (context.Plan.RootFolder,“工具箱”));结束函数mexTask (~)编译mex文件墨西哥人墨西哥人/ convec.c-outdir工具箱/结束函数testTask (~)%运行单元测试结果=运行测试(“测试”);disp(结果);assertSuccess(结果);结束

好了,我要送你们开始你们的MATLAB项目开发冒险用新的构建工具。我们希望听到您的反馈。让我们做一个系列吧!我将在博客上多写几次,这样您就可以看到这个项目在功能上的增长,并真正开始利用这个构建框架。此外,我们正在疯狂地开发这个工具的未来功能。因此,从多个方面来看,这只是未来更多事情的开始。




发布与MATLAB®R2022b

|

评论

如欲留言,请点击在这里登录到您的MathWorks帐户或创建一个新帐户。

Baidu
map