{"id":2835,"date":"2022-10-17T12:34:02","date_gmt":"2022-10-17T16:34:02","guid":{"rendered":"https:\/\/blogs.mathworks.com\/developer\/?p=2835"},"modified":"2022-10-17T14:22:34","modified_gmt":"2022-10-17T18:22:34","slug":"building-blocks-with-buildtool","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/developer\/2022\/10\/17\/building-blocks-with-buildtool\/","title":{"rendered":"Building Blocks"},"content":{"rendered":"\r\n\r\n<div class=\"content\"><p>My people! Oh how I have missed you. It has been such a long time since we have talked about some developer workflow goodness here on the blog. I have found it hard to sit down and write up more thoughts and musings on these topics, but the silver lining here is a big reason for my lack of time is that we have been hard at work delivering development infrastructure for MATLAB.<\/p><p>One of those things is the new build tool for MATLAB that included in R2022b! We are super excited about this tool's rookie release, but even more excited for all the value that will come of it as you begin using it for your MATLAB projects.<\/p><p>What is this thing anyway? Well in short it is a standard interface for you to build and collaborate on your MATLAB projects. \"Build?\", you say?<\/p><p>Yes, \"Build!\", I say. Anyone developing serious, shareable, production grade MATLAB code knows that even though MATLAB is an \"easy-to-leverage\" language that typically doesn't require an actual \"compile\" step, it still requires a development process that includes tasks like testing, quality gates, and bumping a release number. Also it turns out that there are many ways in which MATLAB does indeed <i><b>build<\/b><\/i> something. Think mex files, p-code, code generation, toolbox packages, doc pages, or producing artifacts from MATLAB Compiler and Compiler SDK. These are all build steps.<\/p><p>The issue though, has been that there has been no standard API for MATLAB projects to organize these build steps. It usually ends up looking something like this:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"http:\/\/blogs.mathworks.com\/developer\/files\/y2022JustAdHocScripts.png\" alt=\"\"> <\/p><p>Does this look familiar? It does to me. All of these scripts grow in a project or repo for doing these specific tasks. Each one looks a little different because one was written on Tuesday and the other the following Monday. If we are lucky, we remember how these all work when we need to interact with them. However, sometimes we are not lucky. Sometimes we go back to our code and haven't the foggiest idea how we built it, in what order, and with which scripts.<\/p><p>Also, know who is <i>never<\/i> so lucky? A new contributor. Someone who wants to contribute to your code and hasn't learned the system you have put in place to develop the project. Some projects are rigorous and do indeed have their own custom-authored build framework put in place. This is great for them, but requires more maintenance, and even in these cases a new developer on the project needs to learn this custom system, which is different than all the other systems to build MATLAB code.<\/p><p>Well, not anymore. Starting in <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/build-automation.html?s_tid=CRUX_lftnav\">R2022b we now have a standard interface and build framework<\/a> that enables project owners to easily produce their build in a way that anyone else can consume, no matter how complicated the build pipeline is. We now can move from ad-hoc scripts and custom build systems to a known, structured, and standard framework.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"http:\/\/blogs.mathworks.com\/developer\/files\/y2022AdHoc2BuildTool.png\" alt=\"\"> <\/p><p>Let's take my favorite simple Mass-Spring-Damper example (looks like I am still a mechanical engineer at heart). This is a simple example \"toolbox\" that has 3 components, a design script <b><tt>springMassDamperDesign.m<\/tt><\/b> that defines stiffness and damping constants for the system, a function <b><tt>simulateSystem.m<\/tt><\/b> that simulates the system from an initial condition outside of equilibrium to show a step response, and a mex file <b><tt>convec.c<\/tt><\/b> that convolves two arrays, which might be a useful utility for a dynamic system such as this. It also has a couple tests to ensure all is well and good as the code changes.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"http:\/\/blogs.mathworks.com\/developer\/files\/y2022ToolboxFoldersAndFiles.png\" alt=\"\"> <\/p><p>Hopefully the author of this code knows all about these components and why they were written as they were. However, if I am a contributor for the first time to this code base I have no idea. My workflow might look something like this:<\/p><div><ol><li>Get the code<\/li><li>Use the toolbox<\/li><li>See there is something I want to change about the toolbox, a feature to add or a tweak to the design<\/li><li>Make the change<\/li><li>Submit the change for the win!!<\/li><\/ol><\/div><p>Seems like I am setting myself up for a solid contribution, and I am very proud of myself. After getting the code I see the initial design looks like so:<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> design = springMassDamperDesign(mass)\r\n\r\n<span class=\"keyword\">if<\/span> nargin\r\n  m = mass;\r\n<span class=\"keyword\">else<\/span>\r\n  m = 1500; <span class=\"comment\">% Need to know the mass to determine critical damping<\/span>\r\n<span class=\"keyword\">end<\/span>\r\n\r\ndesign.k = 5e6; <span class=\"comment\">% Spring Constant<\/span>\r\ndesign.c = 5e5; <span class=\"comment\">% Damping coefficient<\/span>\r\n\r\n\r\n<\/pre><p>...and when simulating using the included function:<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> [x, t] = simulateSystem(design)\r\n\r\n<span class=\"keyword\">if<\/span> ~isstruct(design) || ~all(isfield(design,{<span class=\"string\">'c'<\/span>,<span class=\"string\">'k'<\/span>}))\r\n    error(<span class=\"string\">'simulateSystem:InvalidDesign:ShouldBeStruct'<\/span>, <span class=\"keyword\">...<\/span>\r\n        <span class=\"string\">'The design should be a structure with fields \"c\" and \"k\"'<\/span>);\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"comment\">% Design variables<\/span>\r\nc = design.c;\r\nk = design.k;\r\n\r\n<span class=\"comment\">% Constant variables<\/span>\r\nz0 = [-0.1; 0];  <span class=\"comment\">% Initial Position and Velocity<\/span>\r\nm = 1500;        <span class=\"comment\">% Mass<\/span>\r\n\r\nodefun = @(t,z) [0 1; -k\/m -c\/m]*z;\r\n[t, z] = ode45(odefun, [0, 1], z0);\r\n\r\n<span class=\"comment\">% The first column is the position (displacement from equilibrium)<\/span>\r\nx = z(:, 1);\r\n\r\n<\/pre><p>...it yields the following response:<\/p><pre class=\"codeinput\">[t,y] = simulateSystem(springMassDamperDesign);\r\nplot(y,t)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"http:\/\/blogs.mathworks.com\/developer\/files\/y2022BuildingBlocks_01.png\" alt=\"\"> <p>Pretty decent, but I think that there is room for improvement. I think we can get back to equilibrium sooner, and I like a nice smooth shift that slightly overshoots. Because I am an excellent mechanical engineer, this is a clearly preferable design, we just need to have a little less damping:<\/p><pre class=\"codeinput\">addpath <span class=\"string\">.changes\/round1<\/span>\r\n<\/pre><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> design = springMassDamperDesign(mass)\r\n\r\n<span class=\"keyword\">if<\/span> nargin\r\n  m = mass;\r\n<span class=\"keyword\">else<\/span>\r\n  m = 1500; <span class=\"comment\">% Need to know the mass to determine critical damping<\/span>\r\n<span class=\"keyword\">end<\/span>\r\n\r\ndesign.k = 5e6; <span class=\"comment\">% Spring Constant<\/span>\r\ndesign.c = 1e5; <span class=\"comment\">% Damping coefficient<\/span>\r\n\r\n\r\n<\/pre><pre class=\"codeinput\">[t,y] = simulateSystem(springMassDamperDesign);\r\nplot(y,t)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"http:\/\/blogs.mathworks.com\/developer\/files\/y2022BuildingBlocks_02.png\" alt=\"\"> <p>...and submit. This is when I get a dose of humility and experience a world of pain. The toolbox maintainer declines my submission because this design fails a test already put in place intended to limit the overshoot of the response. See?<\/p><pre class=\"codeinput\">runtests(<span class=\"string\">\"tests\/designTest.m\"<\/span>)\r\n<\/pre><pre class=\"codeoutput\">Running designTest\r\n.\r\n================================================================================\r\nVerification failed in designTest\/testOvershoot.\r\n    ----------------\r\n    Test Diagnostic:\r\n    ----------------\r\n    Overshoot violation! Maximum overshoot is 0.01\r\n    ---------------------\r\n    Framework Diagnostic:\r\n    ---------------------\r\n    verifyLessThan failed.\r\n    --&gt; The value must be less than the maximum value.\r\n    \r\n    Actual Value:\r\n       0.010846982843858\r\n    Maximum Value (Exclusive):\r\n       0.010000000000000\r\n    ------------------\r\n    Stack Information:\r\n    ------------------\r\n    In \/Users\/acampbel\/Library\/CloudStorage\/OneDrive-MathWorks\/repos\/msd_blog1\/tests\/designTest.m (testOvershoot) at 23\r\n================================================================================\r\n..\r\nDone designTest\r\n__________\r\n\r\nFailure Summary:\r\n\r\n     Name                      Failed  Incomplete  Reason(s)\r\n    =======================================================================\r\n     designTest\/testOvershoot    X                 Failed by verification.\r\n\r\nans = \r\n\r\n  1&times;3 TestResult array with properties:\r\n\r\n    Name\r\n    Passed\r\n    Failed\r\n    Incomplete\r\n    Duration\r\n    Details\r\n\r\nTotals:\r\n   2 Passed, 1 Failed, 0 Incomplete.\r\n   0.052733 seconds testing time.\r\n\r\n<\/pre><p>In retrospect this is easy to predict, there were tests after all. I should have run them before submitting. But there was nothing pointing me in their direction and I just missed it. For a simple example repo that might seem obvious, but for a \"real\" toolbox this can be hard to see.<\/p><p>Alright clearly there is more work to do after my contribution was declined tersely by an overworked toolbox author. But I am still up to the task. After learning that there is an overshoot requirement I can tweak my design to fit within these constraints:<\/p><pre class=\"codeinput\">addpath <span class=\"string\">.changes\/round2<\/span>\r\n<\/pre><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> design = springMassDamperDesign(mass)\r\n\r\n<span class=\"keyword\">if<\/span> nargin\r\n  m = mass;\r\n<span class=\"keyword\">else<\/span>\r\n  m = 1500; <span class=\"comment\">% Need to know the mass to determine critical damping<\/span>\r\n<span class=\"keyword\">end<\/span>\r\n\r\ndesign.k = 5e6; <span class=\"comment\">% Spring Constant<\/span>\r\ndesign.c = 1.1e5; <span class=\"comment\">% Damping coefficient<\/span>\r\n\r\n\r\n<\/pre><pre class=\"codeinput\">[t,y] = simulateSystem(springMassDamperDesign);\r\nplot(y,t)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"http:\/\/blogs.mathworks.com\/developer\/files\/y2022BuildingBlocks_03.png\" alt=\"\"> <p>Looks good, does it pass the test?<\/p><pre class=\"codeinput\">runtests(<span class=\"string\">\"tests\/designTest.m\"<\/span>)\r\n<\/pre><pre class=\"codeoutput\">Running designTest\r\n...\r\nDone designTest\r\n__________\r\n\r\n\r\nans = \r\n\r\n  1&times;3 TestResult array with properties:\r\n\r\n    Name\r\n    Passed\r\n    Failed\r\n    Incomplete\r\n    Duration\r\n    Details\r\n\r\nTotals:\r\n   3 Passed, 0 Failed, 0 Incomplete.\r\n   0.011899 seconds testing time.\r\n\r\n<\/pre><p>Yes! Finally I must be done. However, when I submit this code I get another rejection because there is still a test failing for the mex file utility that I didn't even know about:<\/p><pre class=\"codeinput\">runtests(<span class=\"string\">\"tests\"<\/span>)\r\n<\/pre><pre class=\"codeoutput\">Running convecTest\r\n\r\n================================================================================\r\nError occurred in convecTest\/MatchesConvBaseline and it did not run to completion.\r\n    ---------\r\n    Error ID:\r\n    ---------\r\n    'MATLAB:UndefinedFunction'\r\n    --------------\r\n    Error Details:\r\n    --------------\r\n    Undefined function 'convec' for input arguments of type 'double'.\r\n    \r\n    Error in convecTest (line 6)\r\n    assert(isequal(convec(x,y), conv(x,y)), ...\r\n================================================================================\r\n.\r\nDone convecTest\r\n__________\r\n\r\nRunning designTest\r\n...\r\nDone designTest\r\n__________\r\n\r\nFailure Summary:\r\n\r\n     Name                            Failed  Incomplete  Reason(s)\r\n    ===============================================================\r\n     convecTest\/MatchesConvBaseline    X         X       Errored.\r\n\r\nans = \r\n\r\n  1&times;4 TestResult array with properties:\r\n\r\n    Name\r\n    Passed\r\n    Failed\r\n    Incomplete\r\n    Duration\r\n    Details\r\n\r\nTotals:\r\n   3 Passed, 1 Failed, 1 Incomplete.\r\n   0.036491 seconds testing time.\r\n\r\n<\/pre><p>Alright, at this point I see that there is some utility that I wasn't changing, using, or even familiar with and it's test is failing. Furthermore, I realize that it is failing because it isn't compiled. I have no idea how to compile this mex file, and at this point I give up because I hadn't planned to invest this much time into this contribution. I don't have time to learn all the details of this repo (I just wanted to tweak the damping coefficient!). After giving up I leave with a bad taste in my mouth. I am probably done trying to contribute to this code base, and actually may even think twice before trying to contribute to some other code base. Not good. No buena. Nicht gut.<\/p><p><b>Enter buildtool<\/b><\/p><p>All of this pain can be addressed through using this new build tool. As a new contributor, all I need to know is that I need to invoke the build tool to go through the author's intended development workflow. I need to learn this the first time, but once I am familiar with this standard framework I can interact with <b>any other project<\/b> that is also using the build tool. Once I see that the root of the project has a file called <b><tt>buildfile.m<\/tt><\/b> I know I am in business and I can do anything the author intended, including things like running tests and compiling mex files, by simply invoking the tool. Let's try it:<\/p><pre class=\"codeinput\">buildtool\r\n<\/pre><pre class=\"codeoutput\">** Starting mex\r\nBuilding with 'Xcode with Clang'.\r\nMEX completed successfully.\r\n** Finished mex\r\n\r\n** Starting setup\r\n** Finished setup\r\n\r\n** Starting test\r\nRunning convecTest\r\n.\r\nDone convecTest\r\n__________\r\n\r\nRunning designTest\r\n...\r\nDone designTest\r\n__________\r\n\r\n  1&times;4 TestResult array with properties:\r\n\r\n    Name\r\n    Passed\r\n    Failed\r\n    Incomplete\r\n    Duration\r\n    Details\r\n\r\nTotals:\r\n   4 Passed, 0 Failed, 0 Incomplete.\r\n   0.16678 seconds testing time.\r\n\r\n** Finished test\r\n\r\n<\/pre><p>Isn't that beautiful? I didn't have to know anything about how the project is built and I could get rolling quickly. I can make my small change, everything that needs to happen (e.g. building a mex file) happens and then we can confirm it doesn't fail the tests. <b>It makes baking in high quality easy(er).<\/b><\/p><p><b>How's it done?<\/b><\/p><p>I have been focusing on the perspective of the unfamiliar contributor. How can the author\/owner use this to set up for success? Well this is super simple and leverages an easy to work with scriptable MATLAB interface as the fundamental framework. You start by creating your <b><tt>buildfile.m<\/tt><\/b>, which is a function that creates your build plan.<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> plan = buildfile\r\n\r\nplan = buildplan(localfunctions);\r\n\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><p>Passing all of the local functions when you create your build plan makes it easy to define simple tasks. This enables you to create tasks from any function that ends in the word <b><tt>Task<\/tt><\/b> (or <b><tt>task<\/tt><\/b> or <b><tt>_task<\/tt><\/b> or <b><tt>tAsK<\/tt><\/b>, etc). The first comment in the function (the H1 line) gives a task description. For this case we have 3 tasks we'd like to add.<\/p><p><b>A setup task<\/b><\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> setupTask(context)\r\n<span class=\"comment\">% Setup path for the build<\/span>\r\naddpath(fullfile(context.Plan.RootFolder,<span class=\"string\">\"toolbox\"<\/span>));\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><p>This task ensures that the right paths are in place for the build. You might ask whether this should be done using a MATLAB Project, and the answer is yes absolutely! That is a better way. For now we are building this in but will projectify it in a later post.<\/p><p><b>A mex task<\/b><\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> mexTask(~)\r\n<span class=\"comment\">% Compile mex files<\/span>\r\n\r\nmex <span class=\"string\">mex\/convec.c<\/span> <span class=\"string\">-outdir<\/span> <span class=\"string\">toolbox\/<\/span>;\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><p>This is a pretty simple compile in this example, but for many projects this step can be more involved. Simple or complex, here is where you can make it trivial for the newcomer.<\/p><p><b>A test task<\/b><\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> testTask(~)\r\n<span class=\"comment\">% Run the unit tests<\/span>\r\n\r\nresults = runtests(<span class=\"string\">\"tests\"<\/span>);\r\ndisp(results);\r\nassertSuccess(results);\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><p>Straightforward. Now that those tasks are defined and automatically included in your build file anyone can see what tasks can be run:<\/p><pre class=\"codeinput\">buildtool <span class=\"string\">-tasks<\/span>\r\n<\/pre><pre class=\"codeoutput\">mex   - Compile mex files\r\nsetup - Setup path for the build\r\ntest  - Run the unit tests\r\n\r\n<\/pre><p>Great, we can see our 3 tasks, but as you might predict that these tasks can't be run in just any order. The tests won't pass unless the proper code is on the path and the mex file is built. These task dependency relationships can be defined in the main function as you setup your plan. We need to add these dependencies, and while we are at it, let's setup a default task so that <b><tt>buildtool<\/tt><\/b> will work without even passing any arguments.<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> plan = buildfile\r\n\r\nplan = buildplan(localfunctions);\r\n\r\nplan(<span class=\"string\">\"test\"<\/span>).Dependencies = [<span class=\"string\">\"mex\"<\/span>, <span class=\"string\">\"setup\"<\/span>];\r\n\r\nplan.DefaultTasks = <span class=\"string\">\"test\"<\/span>;\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><p>Now we can invoke it by default by just calling <b><tt>buildtool<\/tt><\/b> (as we did above) or we can invoke a specific task we'd like to run such as mex and it will just run what is required for that task:<\/p><pre class=\"codeinput\">buildtool <span class=\"string\">mex<\/span>\r\n<\/pre><pre class=\"codeoutput\">** Starting mex\r\nBuilding with 'Xcode with Clang'.\r\nMEX completed successfully.\r\n** Finished mex\r\n\r\n<\/pre><p>Here is the full buildfile for your reference:<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> plan = buildfile\r\n\r\nplan = buildplan(localfunctions);\r\n\r\nplan(<span class=\"string\">\"test\"<\/span>).Dependencies = [<span class=\"string\">\"mex\"<\/span>, <span class=\"string\">\"setup\"<\/span>];\r\n\r\nplan.DefaultTasks = <span class=\"string\">\"test\"<\/span>;\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"keyword\">function<\/span> setupTask(context)\r\n<span class=\"comment\">% Setup path for the build<\/span>\r\naddpath(fullfile(context.Plan.RootFolder,<span class=\"string\">\"toolbox\"<\/span>));\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"keyword\">function<\/span> mexTask(~)\r\n<span class=\"comment\">% Compile mex files<\/span>\r\n\r\nmex <span class=\"string\">mex\/convec.c<\/span> <span class=\"string\">-outdir<\/span> <span class=\"string\">toolbox\/<\/span>;\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"keyword\">function<\/span> testTask(~)\r\n<span class=\"comment\">% Run the unit tests<\/span>\r\n\r\nresults = runtests(<span class=\"string\">\"tests\"<\/span>);\r\ndisp(results);\r\nassertSuccess(results);\r\n<span class=\"keyword\">end<\/span>\r\n\r\n\r\n<\/pre><p>Alright with that I am going or send you off to begin your MATLAB project development adventures with the new build tool. We'd love to hear your feedback. Let's make this a series! I am going to blog a few more times on this so you can see this project grow in capabilities and really start to leverage this build framework. Also, we are working like crazy on future capabilities for this tool. So on multiple fronts this is just the beginning of much more to come.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_ed8c9b355938455bb07502a6f76c17f1() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='ed8c9b355938455bb07502a6f76c17f1 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' ed8c9b355938455bb07502a6f76c17f1';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        copyright = 'Copyright 2022 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n\r\n        d.title = title + ' (MATLAB code)';\r\n        d.close();\r\n    }   \r\n     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_ed8c9b355938455bb07502a6f76c17f1()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2022b<br><\/p><\/div><!--\r\ned8c9b355938455bb07502a6f76c17f1 ##### SOURCE BEGIN #####\r\n%% \r\n% My people! Oh how I have missed you. It has been such a long time since\r\n% we have talked about some developer workflow goodness here on the blog. I\r\n% have found it hard to sit down and write up more thoughts and musings on\r\n% these topics, but the silver lining here is a big reason for my lack of\r\n% time is that we have been hard at work delivering development\r\n% infrastructure for MATLAB.\r\n%\r\n% One of those things is the new build tool for MATLAB that included in\r\n% R2022b! We are super excited about this tool's rookie release, but\r\n% even more excited for all the value that will come of it as you begin\r\n% using it for your MATLAB projects. \r\n%\r\n% What is this thing anyway? Well in short it is a standard interface for\r\n% you to build and collaborate on your MATLAB projects. \"Build?\", you say?\r\n%\r\n% Yes, \"Build!\", I say. Anyone developing serious, shareable, production\r\n% grade MATLAB code knows that even though MATLAB is an \"easy-to-leverage\"\r\n% language that typically doesn't require an actual \"compile\" step, it\r\n% still requires a development process that includes tasks like testing,\r\n% quality gates, and bumping a release number. Also it turns out that there\r\n% are many ways in which MATLAB does indeed _*build*_ something. Think mex\r\n% files, p-code, code generation, toolbox packages, doc pages, or producing\r\n% artifacts from MATLAB Compiler and Compiler SDK. These are all build\r\n% steps.\r\n%\r\n% The issue though, has been that there has been no standard API for MATLAB\r\n% projects to organize these build steps. It usually ends up looking\r\n% something like this:\r\n%\r\n% <<y2022JustAdHocScripts.png>>\r\n% \r\n% Does this look familiar? It does to me. All of these scripts grow in a\r\n% project or repo for doing these specific tasks. Each one looks a little\r\n% different because one was written on Tuesday and the other the following\r\n% Monday. If we are lucky, we remember how these all work when we need to\r\n% interact with them. However, sometimes we are not lucky. Sometimes we go\r\n% back to our code and haven't the foggiest idea how we built it, in what\r\n% order, and with which scripts. \r\n%\r\n% Also, know who is _never_ so lucky? A new contributor. Someone who wants\r\n% to contribute to your code and hasn't learned the system you have put in\r\n% place to develop the project. Some projects are rigorous and do indeed\r\n% have their own custom-authored build framework put in place. This is\r\n% great for them, but requires more maintenance, and even in these cases a\r\n% new developer on the project needs to learn this custom system, which is\r\n% different than all the other systems to build MATLAB code.\r\n%\r\n% Well, not anymore. Starting in\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/build-automation.html?s_tid=CRUX_lftnav\r\n% R2022b we now have a standard interface and build framework> that enables\r\n% project owners to easily produce their build in a way that anyone else\r\n% can consume, no matter how complicated the build pipeline is. We now can\r\n% move from ad-hoc scripts and custom build systems to a known, structured,\r\n% and standard framework.\r\n%\r\n% <<y2022AdHoc2BuildTool.png>>\r\n%\r\n% Let's take my favorite simple Mass-Spring-Damper example (looks like I am\r\n% still a mechanical engineer at heart). This is a simple example \"toolbox\"\r\n% that has 3 components, a design script *|springMassDamperDesign.m|* that\r\n% defines stiffness and damping constants for the system, a function\r\n% *|simulateSystem.m|* that simulates the system from an initial condition\r\n% outside of equilibrium to show a step response, and a mex file\r\n% *|convec.c|* that convolves two arrays, which might be a useful utility\r\n% for a dynamic system such as this. It also has a couple tests to ensure\r\n% all is well and good as the code changes.\r\n%\r\n% <<y2022ToolboxFoldersAndFiles.png>>\r\n%\r\n% Hopefully the author of this code knows all about these components and\r\n% why they were written as they were. However, if I am a contributor for\r\n% the first time to this code base I have no idea. My workflow might look\r\n% something like this:\r\n%\r\n% # Get the code\r\n% # Use the toolbox\r\n% # See there is something I want to change about the toolbox, a feature to\r\n% add or a tweak to the design\r\n% # Make the change\r\n% # Submit the change for the win!!\r\n% \r\n% Seems like I am setting myself up for a solid contribution, and I am very\r\n% proud of myself. After getting the code I see the initial design looks\r\n% like so:\r\n%\r\n% <include>springMassDamperDesign.m<\/include>\r\n%\r\n% ...and when simulating using the included function:\r\n% \r\n% <include>simulateSystem.m<\/include>\r\n% \r\n% ...it yields the following response:\r\n[t,y] = simulateSystem(springMassDamperDesign);\r\nplot(y,t)\r\n\r\n%%\r\n% Pretty decent, but I think that there is room for improvement. I think we\r\n% can get back to equilibrium sooner, and I like a nice smooth shift that\r\n% slightly overshoots. Because I am an excellent mechanical engineer, this\r\n% is a clearly preferable design, we just need to have a little less\r\n% damping: \r\n%\r\naddpath .changes\/round1\r\n\r\n%%\r\n% <include>.changes\/round1\/springMassDamperDesign.m<\/include>\r\n%\r\n[t,y] = simulateSystem(springMassDamperDesign);\r\nplot(y,t)\r\n\r\n%% \r\n% ...and submit. This is when I get a dose of humility and experience a\r\n% world of pain. The toolbox maintainer declines my submission because this\r\n% design fails a test already put in place intended to limit the overshoot\r\n% of the response. See?\r\nruntests(\"tests\/designTest.m\")\r\n\r\n%%\r\n% In retrospect this is easy to predict, there were tests after all. I\r\n% should have run them before submitting. But there was nothing pointing me\r\n% in their direction and I just missed it. For a simple example repo that\r\n% might seem obvious, but for a \"real\" toolbox this can be hard to see.\r\n% \r\n% Alright clearly there is more work to do after my contribution was\r\n% declined tersely by an overworked toolbox author. But I am still up to\r\n% the task. After learning that there is an overshoot requirement I can\r\n% tweak my design to fit within these constraints:\r\naddpath .changes\/round2\r\n\r\n%%\r\n% <include>.changes\/round2\/springMassDamperDesign.m<\/include>\r\n%\r\n[t,y] = simulateSystem(springMassDamperDesign);\r\nplot(y,t)\r\n\r\n%%\r\n% Looks good, does it pass the test?\r\nruntests(\"tests\/designTest.m\")\r\n\r\n%% \r\n% Yes! Finally I must be done. However, when I submit this code I get\r\n% another rejection because there is still a test failing for the mex file\r\n% utility that I didn't even know about:\r\nruntests(\"tests\")\r\n\r\n%% \r\n% Alright, at this point I see that there is some utility that I wasn't\r\n% changing, using, or even familiar with and it's test is failing.\r\n% Furthermore, I realize that it is failing because it isn't compiled. I\r\n% have no idea how to compile this mex file, and at this point I give up\r\n% because I hadn't planned to invest this much time into this contribution.\r\n% I don't have time to learn all the details of this repo (I just wanted to\r\n% tweak the damping coefficient!). After giving up I leave with a bad taste\r\n% in my mouth. I am probably done trying to contribute to this code base,\r\n% and actually may even think twice before trying to contribute to some\r\n% other code base. Not good. No buena. Nicht gut.\r\n\r\n%%\r\n% *Enter buildtool* \r\n%\r\n% All of this pain can be addressed through using this new build tool. As a\r\n% new contributor, all I need to know is that I need to invoke the build\r\n% tool to go through the author's intended development workflow. I need to\r\n% learn this the first time, but once I am familiar with this standard\r\n% framework I can interact with *any other project* that is also using the\r\n% build tool. Once I see that the root of the project has a file called\r\n% *|buildfile.m|* I know I am in business and I can do anything the author\r\n% intended, including things like running tests and compiling mex files, by\r\n% simply invoking the tool. Let's try it:\r\nbuildtool\r\n\r\n%%\r\n% Isn't that beautiful? I didn't have to know anything about how the project\r\n% is built and I could get rolling quickly. I can make my small change,\r\n% everything that needs to happen (e.g. building a mex file) happens and\r\n% then we can confirm it doesn't fail the tests. *It makes baking in high\r\n% quality easy(er).*\r\n%\r\n%%\r\n%\r\n% *How's it done?* \r\n%\r\n% I have been focusing on the perspective of the unfamiliar contributor.\r\n% How can the author\/owner use this to set up for success? Well this is\r\n% super simple and leverages an easy to work with scriptable MATLAB\r\n% interface as the fundamental framework. You start by creating your\r\n% *|buildfile.m|*, which is a function that creates your build plan.\r\n%\r\n% <include>.hidden\/buildfileSnippet1.m<\/include>\r\n%\r\n% Passing all of the local functions when you create your build plan makes\r\n% it easy to define simple tasks. This enables you to create tasks from any\r\n% function that ends in the word *|Task|* (or *|task|* or *|_task|* or\r\n% *|tAsK|*, etc). The first comment in the function (the H1 line) gives a\r\n% task description. For this case we have 3 tasks we'd like to add.\r\n%\r\n%%\r\n% *A setup task*\r\n%\r\n% <include>.hidden\/buildfileSnippet2.m<\/include>\r\n%\r\n% This task ensures that the right paths are in place for the build. You\r\n% might ask whether this should be done using a MATLAB Project, and the\r\n% answer is yes absolutely! That is a better way. For now we are building this\r\n% in but will projectify it in a later post.\r\n%\r\n% *A mex task*\r\n%\r\n% <include>.hidden\/buildfileSnippet3.m<\/include>\r\n%\r\n% This is a pretty simple compile in this example, but for many projects\r\n% this step can be more involved. Simple or complex, here is where you\r\n% can make it trivial for the newcomer.\r\n%\r\n% *A test task*\r\n%\r\n% <include>.hidden\/buildfileSnippet4.m<\/include>\r\n%\r\n% Straightforward. Now that those tasks are defined and automatically\r\n% included in your build file anyone can see what tasks can be run:\r\nbuildtool -tasks\r\n\r\n%% \r\n% Great, we can see our 3 tasks, but as you might predict that these tasks\r\n% can't be run in just any order. The tests won't pass unless the proper\r\n% code is on the path and the mex file is built. These task dependency\r\n% relationships can be defined in the main function as you setup your plan.\r\n% We need to add these dependencies, and while we are at it, let's setup a\r\n% default task so that\r\n% *|buildtool|* will work without even passing any arguments.\r\n%\r\n% <include>.hidden\/buildfileSnippet5.m<\/include>\r\n%\r\n% Now we can invoke it by default by just calling *|buildtool|* (as we did\r\n% above) or we can invoke a specific task we'd like to run such as mex and\r\n% it will just run what is required for that task:\r\nbuildtool mex\r\n\r\n%% \r\n% Here is the full buildfile for your reference:\r\n%\r\n% <include>buildfile.m<\/include>\r\n%\r\n% Alright with that I am going or send you off to begin your MATLAB project\r\n% development adventures with the new build tool. We'd love to hear your\r\n% feedback. Let's make this a series! I am going to blog a few more times\r\n% on this so you can see this project grow in capabilities and really start\r\n% to leverage this build framework. Also, we are working like crazy on\r\n% future capabilities for this tool. So on multiple fronts this is just the\r\n% beginning of much more to come.\r\n\r\n##### SOURCE END ##### ed8c9b355938455bb07502a6f76c17f1\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2022AdHoc2BuildTool.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>\r\n\r\nMy people! Oh how I have missed you. It has been such a long time since we have talked about some developer workflow goodness here on the blog. I have found it hard to sit down and write up more... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/developer\/2022\/10\/17\/building-blocks-with-buildtool\/\">read more >><\/a><\/p>","protected":false},"author":90,"featured_media":2868,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[39,4,28,20,7,33],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/2835"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/users\/90"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/comments?post=2835"}],"version-history":[{"count":19,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/2835\/revisions"}],"predecessor-version":[{"id":2931,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/2835\/revisions\/2931"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media\/2868"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media?parent=2835"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/categories?post=2835"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/tags?post=2835"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}