 Hi, welcome to Visual Studio Toolbox. I'm your host, Robert Green, and joining me today is Daniel Plasted. Hello. Daniel, we're going to talk about MS Build today and some of the new things that are in the build system. Yep. So, very much to talk about, a bunch of changes, a bunch of thought has been going into this, which some people might think, oh, I hit build. What do I need to be concerned about? But it's actually a fair amount that's gone on under the covers. Oh yeah, and far more than we'll cover here. Cool. Yeah. So yeah, we'll cover several different things, but some of them have been more covered than others. So I wanted to start out with structured logs and structured log viewer for MS Build. And this is something that if you have ever, have you written MS Build custom tasks and targets? No, I have not. OK, if you've ever done that or if you've ever tried to debug, you know, why is the build not doing what I expect it to do, this will be amazing to you. I usually think if the build is not working, it's because of something I did wrong. If it is something you did wrong, then this would help you investigate why. And we're not talking about compiler errors there. I mean, those are kind of well understood. You double click on it, and it shows you wearing your source code. But this is if you're extending the build, if you have a process to maybe you're using MS Build to create new get packages, or you're injecting a tool in there somewhere to generate code for you, or a host of other things, or if you're using a new get package to do that. So I have this project here, and I've built it. And what I've done, I've built it from the command line, and I've just added the flag slash bl. And that stands for binary log. So that's going to create a binary log for me. And so I have it here, msbuild.binlog. So now I'm going to, that is built into MS Build as of update three of Visual Studio 2017. So does that happen automatically now? Or is that something to ask for inside Visual Studio? So inside Visual Studio, you don't currently get this. So this is more like if you want to debug it, you do a command line build. Got it. And so there's different flags for how to log. And this is a new one that says create a binary log, so slash bl. And that'll create msbuild.binlog. And then there's a tool which is an open source project by Kiril Asenkov who works for Microsoft, which then gives you a view of this log. And so you can see a tree of what the build did, all of the things that it ran, what the inputs and outputs of those things were. You can do a search. So here I'm going to search for strong name. And I got a whole bunch of results because that's kind of the name of my project. But I'm looking for the target that was actually doing strong namer so I can filter it down by saying $target. And then so it jumps right to that target that ran. And then I could double click on that. And this shows me the definition of this target here. So this is the actual code that was running. And then I can expand down in here and I can see the inputs to this task that was called here, the parameters, I can drill all the way down and see all the data that's flowing through msbuild. And then this is the outputs, the diagnostic messages that task output. And then I'm doing some more stuff here, which I can see. So if you're not customizing msbuild, if you're not writing your own logic, hopefully you never need to do something like this. But I do a whole bunch of that in my job. That's a large part of what I do. And I can't imagine continuing to do it without this tool. It's pretty amazing. And furthermore, this tool is new. I think it was written towards the beginning of this year. That's when I found out about it. But it keeps getting better. This double clicking and going to the source that was added this summer, I think. And I just, I did it and I found out and I'm like, this is amazing. Cool. So how do you get that? So if you wanna know about this tool, just go to msbuildlog.com. And that's got some documentation, the link to download the actual viewer tool. So that's the URL to go to. Okay. Cool. So then moving on from that, let's talk about the journey that we've taken in Project Files. So for, I don't know how long now, four-ish years, we've been working on .NET Core, this open source cross-platform version of .NET. And we wanna be able to compile .NET code. We wanna be able to do that from the command line, from Windows, from Linux, from macOS. And when we started that, msbuild was not cross-platform. And really the msbuild project files weren't great. They weren't kind of appropriate for the experience that we were going for. We were going for a very simple experience, an experience that would appeal to developers who didn't want a lot of tooling. They just wanted to be, use a text editor to edit their code. And so we patterned it a bit after what something like Node.js or any other scripting language or anything would do. And so we had what we called project.json. This was a project format. And so if we look at that, this is what it looked like. And this was only in .NET Core app. So if you hadn't been doing a lot of .NET Core, you wouldn't have necessarily seen this. Yes, you would not see this much unless you were doing .NET Core or a little bit of .NET standard maybe. In Visual Studio 2015 was the time frame when we had this tooling. And so this had a lot of advantages. It was a fairly simple project file. You could imagine typing this into an editor is from scratch, maybe, maybe not, but definitely editing it, making changes in here. It had, it did not list each separate source file. It just, by default, although you could override it, it assumed, okay, I'm just gonna compile all the C-sharp source files in the project folder. And it made it very easy to refer to NuGet packages. This is kind of under the dependencies node. It made it easy to target multiple frameworks. So we have the full .NET framework. Now we have .NET Core, we have .NET standard. Sometimes you wanted to multi-target, compile separately for each one because they have different capabilities. And it made it easy to just say, okay, these are the multiple frameworks I'm targeting. When I build, it builds for each one separately. And another advantage that it had is it was easy to swap out a NuGet package reference with source. So if you're using a library that comes from NuGet, an open source project, and you find a bug in that, then you could easily just clone that repo from GitHub, put it in the right place, and pretty easily just hook it up to your project so that now it's no longer using that package that came from NuGet, you're using the source code. And so you can easily debug it and fix a bug. So that flow was pretty easy. And so a lot of people love this because those were some great new features, a great experience. But as we continued to develop the tooling, we came to the, at the time, unfortunate realization that we just couldn't move forward with this project JSON based tooling. And that's because everything else in .NET, all other .NET projects were built with MS build. And project JSON did not integrate well with MS build. If it's just .NET core, maybe it was okay to have something different. But as .NET standard becomes more and more important and we want you to be able to have libraries that work across all of .NET, then you need references from a .NET framework project or a Xamarin project or UWP project to your .NET standard project. And also from your .NET core project to your .NET standard project. And when those were completely different types of build systems, that didn't work well. We did make it work to some degree, but there were always rough edges and it just ultimately didn't work well. And then you have this whole ecosystem of tooling that understands MS build projects and extensions to Visual Studio, tools that run over your projects. This whole ecosystem didn't apply to this new thing. And the other thing is that we had this new project format, but that meant we basically had a new build engine. We built this new build engine from scratch to handle this, but a build engine is not a simple thing. And so over time, it's like, okay, well, we want parallel builds to be faster so that we can take advantage. And then we want incremental builds. If you make one change in one project, you don't wanna rebuild everything. And people really wanna extend their builds too. They wanna inject in the middle of when it's building or when a project is done building, they wanna run a tool before it gets consumed. And so this didn't really support a whole bunch of extensibility either. So we took a step back and we said, you know, we really have to go back to MS build. But we know that there's lots of great things about this project system. And so we can't just give those up. We have to bring those to MS build. But the funny thing is that we made this decision and we're living in this open source world. And so we went ahead and told everyone, well, yeah, we're gonna move to MS build. And it's gonna be great. We're gonna take all of these things. But to be honest, we didn't know exactly how that was going to work. We had to figure it out. And so we kind of just said, we're going to switch back to MS build and trust us, it will be great. And so if you look at this, this is what you get when you create a new .NET framework project in the old system. So this is what people are used to as far as MS build. So if you go from this simple project.json and you're expecting to go to something like this, this isn't really human edible. And there's a whole bunch of other of those great features that it just doesn't have. And so people didn't really believe us that we were going to be able to get those benefits. But we did manage to bring a whole lot of that. So what we have today now, you create a new project and you have that. And so this is XML, it's still XML. So if you like JSON, sorry about that. But this is much, much simpler. This is something that you can imagine typing from scratch if you had to. And definitely that you can edit, that you can merge and get much easier, et cetera. And all of those benefits, the including your source files by default, not listing them separately, easy references to new get packages, clean project files and all of that, we were able to bring. The one that we didn't entirely get is bringing in new get packages and replacing them with source. That's kind of there, but it's kind of an advanced thing and you have to know how to do it. So that's something that hopefully eventually we can deliver. But yeah, so here we are. And the kind of the philosophy behind this was to have a small project file, simple, easy to edit. That means you're gonna have defaults. You're gonna have conventions that are followed. So the philosophy is have sensible defaults that you can override if you need to. So by- So that's the proge file you get on file new, right? Yes. Okay, and then the one you showed earlier, the console app, so that's, if you create a new console app, you would get that? These are both console apps. So we made a bunch of changes to MSBuild. Right, so the first one's a console app. Yeah, so explain the differences. Yeah, we made a bunch of changes to MSBuild, a bunch of improvements to MSBuild, to Visual Studio and everything. But the focus was we need a project system for .NET Core. So there's lots of great improvements and they applied to .NET Core. They applied to .NET Standard. They are making their way at different rates into standard .NET Framework projects. And so this is what you get if you say create me new .NET Framework project. So that project file is essentially the same as it's always been. This is the same as it's always been. This is create a new .NET Core project. Now, if I want to use all of this new tooling to target .NET Framework, then I just have to change this target framework. So I can say net four six. And now this is a .NET four six console app project. Now, again, this was- Now it's the fact that it's missing a whole bunch of things in the project file. I mean, there's no, are the references in there? How does that work? It's all there by default, right? So this is sensible defaults that you can override if necessary. Got it. So it's all there by default. And this is focused for now on .NET Core scenarios. So there will be some things that don't work depending on what you're doing. If you're just doing a console app, it's probably all going to work. If you want to do a WinForms and WPF app, it's not going to work. You can get it to build if you jump through a few hoops, but don't expect the designers in Visual Studio, for example, to work. So depending on the types of thing and the types of tooling that you need, not all of it supports this new project system. Hopefully that'll grow as time goes on. That's a goal, ultimately. Yes. Now, if you don't spend a lot of time in your project files, maybe you've never actually even looked in the CS project file, it's all kind of irrelevant. Who cares, right? To some degree. Is that a fair statement? There, let's think about what benefits you'll see. So even if you never edit the project file manually, if you're in source control and you have multiple people editing the same project, you might be doing it by adding a reference in Visual Studio, adding a new file. That's going to affect your project file. And especially with the source files all listed, you're potentially going to have merge conflicts. And so this will, since they're just included by default, this would reduce the merge conflicts that you would have in your source control, for example. The other thing is that this brings along with it a new way of referencing new get packages called package reference. And that is expressed more simply in the project file, but also has other advantages with packages.config, which is the original way that new get would reference things. When you reference a package, it takes all of its dependencies and references those directly. So you reference the MVC new get package and that depends on a whole bunch of other MVC sub packages and newtonsoft.json and maybe NV framework. And so you have all of these packages. It walks that graph, but all of them are put directly in your packages.config file. And so you're just left with all of these references and there's no indication of what did you actually reference directly versus what came in because it was a dependency. They're all flattened. So with package reference, you just reference the one thing and that goes in your project file and then it walks the graph as necessary and gets them all in. But if you just wanna say remove that one reference, you just remove that and all of its dependencies will come out. If you wanna upgrade it, you just upgrade that reference and it'll update all of the dependencies, but you don't have this whole churn because the dependency version's changed and all of the things it was bringing in. So that's one benefit of package reference and package reference is available for standard projects in VS 2017. It works a little bit different under the hood but basically it should work the same way and it means that you can get those benefits. And I highly recommend it today to think about migrating towards package reference. So let's look at a demo I have here. So here's an app that I have converted to use this new project system. So the first thing I'm gonna do is I'm just gonna edit the project file. Now, some people never do that, but if you've ever done that, you have to right click here, you have to say unload project and then you can edit the project. Well, no longer here, edit csproj and it just comes up directly. And this continues, this only works with core and standard, this doesn't work with regular framework. For now, yes. For framework or whatever we refer to it. Yeah, for now it's these new style projects which by default is just core and standard. Okay. So here you can see a package reference. I'm referencing a NuGet package and I can go to the NuGet dialog and reference it using the user interface or I can just type here. So package. And if you wanna upgrade it, you can just change that version from 2.0.0 to 2.1 or whatever. You also get IntelliSense. So it's really cool. Not that we don't wanna spend all our lives in the NuGet package manager console, but this is a lot easier. Yeah. Okay, so I've typed in a package reference and you can see I have them over here on the right. I have my dependencies here. So if I save this file, it's going to do a package restore. Oh, that was the wrong. I actually wanted to reference know that time. So now I do it. I save. It's restoring. And then I see I have both of those package references now. So by default it includes all of the source file in this folder, which we can see here. All those are all included here even though they're not directly mentioned here. But if I wanna include something outside of that, I can do that. And in Visual Studio, you would do that by right-clicking on the project and say add existing item and then it's add as link. Yeah. You add something outside of it. Here I can just say compile, include equals and then the relative path to it, which is dot dot slash shared slash. All right, it's not shared. It's common. And that's CS. So likewise, I've added that. And then once I hit save, we'll see that that file will show up over here, probably. Yeah, there we go. So that file showed up there. I might, you know, it's a shared file. Maybe I wanna show it in the sub folder, right? Maybe I wanna group all of my common shared stuff separately. So I can do that with by saying link base. Link is kind of the metadata that's used to specify what path it's gonna be under. So base means use this first and then use kind of the file name or, well, I'll show you, equals common. Okay, so I did that. And so now it's under the common. And just like it's- Did that literally move it? It didn't move it. That's just- You're just linking to it. Yeah, that's just affecting how it's displayed here. It's always been, you know, the actual source file lives in this common folder. And so, you know, maybe I have a common folder and I just want everything in there. I could say star.cs or if I wanted everything recursively, I could say star, whoops. Star star, wax star cs. So this is kind of the pattern matching that says recursively any folder and then everything with a cs extension. And then because it's going recursively under all the folders, then I get the same folder structure displayed under here. Okay, okay. So then let's look at what we can do as far as we have our source files on disk and in the project system. So we can also just rearrange them on disk here. So create a new folder, let's call it source. And then let's say I just want all of my source files to go in there, right? So I just select them, I can drag them in there. And we see that it just picked up that they moved, it updated, I can see them there. Now let's get another source file somewhere else and just copy it in. So I can just copy a file in here and it also shows up, you know, but I had a file on disk, I just didn't really actually want it to be part of the project. And so the way you've always done that is exclude from project, right? And it works here. So it's now excluded from project, but it's done it in a smart way. Previously it kind of support, you could have said in your project file include all files in this directory, but Visual Studio didn't really understand that well. And so if you removed one, it would just replace it with a flat list of all of them except the one you wanted to remove. So now it's smart enough just to, you know, write the exception to the general rule that you have to the project file. And then, okay, I've made a bunch of changes. I wanna go back, I wanna undo everything. So let's just go back to the folder. And so I'm gonna do a get reset or I'm gonna switch get branches and it's just gonna change stuff on disk. So get reset, hard and clean, D. Okay, so get just did a bunch of stuff. Okay. And Visual Studio just reacted to the changes. So if you're, that's another advantage that doesn't have to do specifically with editing the project file. If you're just switching get branches or resetting, Visual Studio would often, if you did that, either show you a bunch of, hey, this changed, do you wanna reload it? Or it just might not work. Now in a lot of cases, it'll just notice it's changed and updated in Visual Studio. Does the same tooling, is the same tooling included in Visual Studio for Mac? Do you know? Not exactly. So Visual Studio for Mac supports these project files. But I don't know as far as the IDE and the tooling exactly how much of the updating and everything that it does, it may support it all. It may not. You might need to reload. I don't know directly. Okay. But it does support .NET Core projects and .NET Standard projects in this format. So you can definitely edit them and use them. Okay, cool. So... And what about Visual Studio code? Same answer? So the thing about VS Code is it's an editor, right? And so you can edit your files in VS Code. It doesn't have a project system. The files it showed is just the files on disk. So it'll show you the files on disk and it'll show you your project file and you can edit your project file from within there. So a lot of this experience will work the same way. It won't have commands to say, right-click, exclude from project file. It won't have the package manager UI which you can use. But it does have all of those editing capabilities and there's the plugin for .NET Omni-Sharp. So it will give you the C-Sharp IntelliSense and everything and commands to build and run and those will all work with this too. Cool. So the one other thing I wanted to show and let's just reset git back to where I was. Okay, so this is going back to all the changes I had made, I guess. But go back to editing that project file and then I'm targeting .NET standard here. That's my target framework. One of the things you could do is target multiple target frameworks. And so to do that, you change target framework to target frameworks plural, right? And then you can have a semi-semit colon separated list. So I could say net 462. And so now this project needs to reload in that case but now it's targeting .NET standard as well as .NET 462. Okay. So we can see that there. And so then when I build that, it's going to build it for both of them. And so if you look at your bin folder, you've got bin, debug is where I am. So then we have the different target frameworks under here. So it's building a different copy of this DLL for each one. So that's standard one for. Is that new capability? Yeah, this is new. This was something that Project JSON supported. Okay. And then now this new style of projects also supports. Cool. Yeah. So we're building for .NET standard one four and for .NET 462. And there's some, you know, other folders because I didn't clean everything I guess. Right. The other thing which I think I forgot to mention at all is it was easy in Project JSON to create a new get package. And so it's also easy here. I don't think I actually, this one is old. That's way old. So you can simply right-click and say pack or I think, yeah, pack. Pack means create a new get package. And you can do that from the command line too. With .NET pack is the command. And so if that worked, then this should be updated. Yep, created today. Yeah. And so this is a new get package and it has these dependencies and we can see it's got a lib folder. And so it's got the library compiled for .NET standard and .NET framework. And they're both packaged up into a new get package and then you, someone can reference that and they'll get the right version depending on what they're targeting .NET framework or .NET standard. So that'd make it easy for somebody to write some standard functionality and then create a new get package out of it and let other people use it fairly easily. That's cool. Yeah, I mean, we've got an ecosystem of new get packages. You know, lots and lots of open source projects. This makes it easier for people to produce those, especially now that we want people to support .NET standard but they might have a separate version for .NET framework or .NET core or Xamarin or whatever. So this helps it make it easier for them. And then, you know, open source is the main, I think, scenario for new get. But if you're inside a large corporation and you've got a team that, you know, creates tools that are used across projects, they, some of those people will create new get packages and consume that just within an enterprise too. Right. So that's cool. So that, there is just one thing that I also wanted to mention and this kind of goes back to, if you're customizing your MS build logic. So often if you have like a repository, you have settings that you wanna apply to all the projects in the repository. And so in MS build, you can import a project from another project. And in the old system, that was explicit. So here we're importing this Microsoft common props, right? And then at the end, we're importing Microsoft C-sharp.target. So that's what a project import looks like. And so often what you would do is if you were setting up a repository with a bunch of projects and you want a common build logic or just common settings, you know, this is the debug configuration or whatever. And this is gonna be the output path where everything goes. You would create one of these MS build files and then you'd put an import there. But that import is kind of hard to write because your projects might be at different levels. And so it's like I'm gonna import dot dot slash dot dot slash dot dot. And I have to have the right number of dots. So that was hard. There was a function you could call in this expression which was get some, I can't remember it's get directory path to file above or something. So there was a way you could say, look up the directory tree until you find a file with this name and return that path. And so that's what people generally did, but it was just this huge long expression that made your project files ugly. So that kind of capability is now built into MS build. So if you have a file named directory dot build dot props or directory dot build dot targets, that will automatically be imported. And so the convention with MS build is that props are the kinds of things that are kind of imported before the body of your project. And targets are the things that are imported at the end. And so now MS build, if you're using the common targets which all dot net projects will, will look for a file called directory dot build dot props to import before it processes the bulk of your project. And a file called directory dot build dot targets after it does the bulk of it. And so if we look in, and so here you can see that we're using that there in one of our projects. Anyway, so that's just kind of a nugget. That's new in MS build 15. And for people who have open source projects or in the enterprise who want a common build system across some projects, that's going to be pretty useful. Cool, cool. That's what I have to cover. All right, awesome. Thanks so much for that. A lot of nice tidbits in there. So even if you, even if you weren't using the core of the standard projects, there's still some good learnings in there. And a lot of the things that we're seeing in there will make it into the classic, is that what we call it now? You can't call it standard. Classic or legacy or existing. Legacy, no. I don't know what words, I'm not going to use that word, that's a bad word. So I don't know what to call all of this stuff. But yeah, a lot of that is, and directory dot build dot props and targets, that works everywhere you're using MS build 15, which is VS 2017 or higher. And the structured logger also works everywhere in update three or higher. Although if you go to the site for the viewer, it's got ways to get it to work with previous versions too, actually. So as far as custom logic and debugging, there's great improvements for anyone. And then lots of great project system and project file improvements for dotnet core and dotnet standard and making their way else. Awesome. All right, thanks so much. Thank you. All right, hope you enjoyed that. We will see you next time on Visual Studio Toolbox.