I'm trying to improve our build process, and to that end I've been looking at turning off copy local and having the whole solution build to a common \bin directory.
What however, is best practise for getting the no longer copied references into the bin directory? I don't want to do this in one of the actual implementation projects as many of them use the same referenced components, and it will mean a proliferation of post build steps.
I know I could create a custom msbuild file but then that would need to be run manually outside of visual studio (I think)? which seems like friction. Is there a way I can create an msbuild project for example, and then have that as part of my solution.
Or is it best just to manage this outside my solution build and have a copy_references.bat file which the dev has to run once to setup their environment getting them into the /bin/debug and /bin/release directories? This seems a bit fragile, but better than checking /bin and the files into svn directly.
One idea I've had is to create an empty c# component project and add the references to it, with copy local turned on. If this was then made a dependency of all other projects it would manage the copying.
Next question is how to manage this with nuget references? My preference is to not check the references into svn, but tell nuget to grab them. So this would also need to be a build step, but again at the solution level.
Additional Info
For a bit more background on why I am evaluating this approach, have a look here:
http://www.ndepend.com/Res%5CNDependWhiteBook_Assembly.pdf
The goal is to massively speed up compilation time by stopping all these redundant copies. Also side benefits if it works might be not having to manually work around the times dependency evaluation doesn't work. Causing one to have to pull referenced assemblies' dependencies into your top level project to ensure they end up in the bin folder.
I suppose in some ways the desire to turn off copy local is an artifact of the inefficiency of the ms build process at both tracing dependencies and evaluating the need to copy things.
You can override the $(OutDir) property globally and keep CopyLocal enabled. Since every project is copying to the same $(OutDir), you won't end up with too much duplication. This is pretty straight forward.
Much more involved, you can also create a shared import file that wires into the standard build and performs a custom post-build deployment. For example,
<Target Name="Deploy"
DependsOnTargets="Deploy)"
AfterTargets="Build">
... copy all output files ...
e.g. use wildcards $(OutDir)\*.dll
e.g. $(OutDir)\$(TargetName)$(TargetExt)
e.g. copy referenced assemblies and copy, see below
</Target>
To get the references, you can call the ResovleAssemblyReferences target and use Returns, or create your own target to get a specific collection as shown in the answer here,
Return the ReferenceCopyLocalPaths from <MSBuild> task
It can be rather involved, but easily configured if you can declare your own "rules" in an item array with metadata.
Related
I apologize if this is trivial, but I'm not a regular VS user and my Google-Fu is turning up nothing obvious or simple.
I have inherited responsibility for a large (500k+ LOC, a dozen solutions, hundreds of projects) repository that's been forked a number of times in the past. The solution/project structure is... spaghetti-esque, in that the filesystem folder structure and the solution/project structure are only weakly correlated, and many projects import/reference other projects outside the filesystem folder hierarchy of their containing solution, and that are not even part of the containing solution.
For example:
c:\SolutionA\SolutionA.sln contains c:\SolutionA\ProjectB.csproj and c:\SolutionA\ProjectC.csproj. But C:\solution\ProjectC.csproj contains a <Import Project="..\SomeOtherRandomSolutionDir\ProjectD.csproj" /> reference.
I know there are a lot of projects/files/resources in this repo that are not used by any of the solutions I'm actually building and I don't need them, but the tentacular nature of the project imports/references makes it hard to determine what's actually necessary for the builds and what's superfluous.
So: is there any relatively simple way to run a solution build in Visual Studio (or MSBuild) and obtain a list of every single file used by the build process? I've tried creating a diagnostic-level build log and grepping[1] it for the repo base path; will that get me what I want? (Narrator: it won't)
EDIT: Assume that all file operations are done entirely by default Visual Studio solution project handling and there's no custom targets or shelling out to copy or move files, in the way Perry Qian describes below
[1] Well, Get-Content | Select-String-ing it, but that's clunkier to say
is there any relatively simple way to run a solution build in Visual
Studio (or MSBuild) and obtain a list of every single file used by the
build process? I've tried creating a diagnostic-level build log and
grepping[1] it for the repo base path; will that get me what I want?
Sorry but I'm afraid this is not supported scenario. You cannot obtain a list of every single files that are used in a project or a solution during build process.
Let me explain it more detailed:
Usually the files which are in the solution explorer are all useful in the project. Since your solution is too large and logically complex, we do not recommend deleting any of the files, and I think they all work.
We can obtain a list of files which are parts of the input items of the projects by MSBuild(usually in <Itemgroup> node of the xxx.csproj file).This is the only way I can think of to get a set of project files through MSBuild. We can add this target into xxx.csproj to list all of them like this:
<Target Name="ShowSingleProjectItemList" AfterTargets="Build">
<Message Importance="high" Text="None file:#(None)---Compile files:#(Compile)---Content files:#(Content)---Embedded Resource files:#(EmbeddedResource)---CodeAnalysisDictionary files:#(CodeAnalysisDictionary)---ApplicationDefinition files:#(ApplicationDefinition)---Page files:#(Page)---Resource files:#(Resource)---SplashScreen files:#(SplashScreen)---DesignData files: #(DesignData) Reference dlls :#(Reference)">
</Target>
Note that this method can only be used for each project and not for the entire solution so if you want to use, add it into every xxx.csproj file.
But for other files which are not as the input items of the projects and added or referenced in the projects by some CMD commands or powershell scripts, build events(Right-click on Project-->Properties-->Build Events)(You can refer to this) and any other custom target in the xxx.csproj,we cannot list all of them by a function.
For example, if you use powershell to do some copy operation like coping some dlls from the path outside of your solution into projects,they can't stay in the project as an item of the project. So we cannot obtain them by MSBuild.
For this situation, we can only manually view all of them that are imported into the projects in whatever way in the diagnostic-level build log.
Conclusion
As input items of the projects, we can get the required files for each project by MSBuild, but for some other operations(powershell,build events,etc) to add files from other path outside into the current project,we cannot retrieve all of their information by a method. You can only look it up one by one by diagnostible-level build log.
Besides,we don't know the structure and logic of the entire solution, so we can't guarantee that every file is an item element, so for now you have to look at it manually.
Update 1
To avoid adding every target into your xxx.csproj(since you have a lot of projects under a solution), you can try to use Directory.Build.props. You just write the custom target into this file and then put the file under your solution. After that, when you build the solution, the build will execute into every project so that you just have to write it once.
Solution
1) create a file namedDirectory.Build.props under the solution
2) write these info into the file
<Target Name="ShowSingleProjectItemList" AfterTargets="Build">
<Message Importance="high" Text="None file:#(None)---Compile files:#(Compile)---Content files:#(Content)---Embedded Resource files:#(EmbeddedResource)---CodeAnalysisDictionary files:#(CodeAnalysisDictionary)---ApplicationDefinition files:#(ApplicationDefinition)---Page files:#(Page)---Resource files:#(Resource)---SplashScreen files:#(SplashScreen)---DesignData files: #(DesignData) Reference dlls :#(Reference)">
</Target>
3) build your solution and you will find the files in the build output window.
I have a project where there are files in a particular non-standard textual format. When these files are touched/modified, I want to run a certain custom compiler on them to generate XML, which is part of the output of the whole solution.
I'm considering creating a MSBuild task to do this. It will take as input the non-stadard file names and output the requisite XML files. The task will then be used in the other projects in the solution.
I want new developers on this project to have minimal setup. That means, I want to be able to take a clean copy of my solution directly from source control and have the build first build the custom task, then apply it as necessary to the other projects in the class.
I'm concerned that the build output of the project that builds the custom task needs to copy its output assembly to some known location so that the other projects can refer to it. What is the proper way of going about doing this?
You're about to walk into a mess here, because Visual Studio is going to lock the custom task Assembly when it's first used, thereby causing any further builds in Visual Studio (i.e. Build > Solution) to fail.
As #stijn commented, you should override the Build target and use another method of building the assembly with the custom task, e.g. using the Csc task or spawning another MSBuild.exe process (see answer to linked question).
The way I decided to go though was to create a separate solution, e.g. "Build Tools", containing the custom task assembly (among other tools), and required that it be built before anything else. I personally find the notion of checking-in prebuilt binaries of this source very unpalatable. If developers didn't want to build the Build Tools solution, they would copy the output from some nightly build.
Unfortunately there isn't an easy way of getting around "hardcoding" a known (relative) location. Using $(SolutionDir) usually works - just not if you try to run MSBuild on the project directly, instead of the solution (VS is a bit more intelligent when you open a project by itself).
I could use some advice.
I'm in the process of adopting subversion, and I'm trying to put some existing Visual Studio 2010 projects into a repository. I have the current version of AhnkSvn.
The projects I have are organised as;
VS2010_projects\Project_A
VS2010_projects\Project_B
VS2010_projects\Project_C
VS2010_projects\Common_code
Where Project_A, Project_B and Project_C may all refer to one or more files in "Common_Code"
In visual studio, these files will have been added using "add as link".
There is no actual project in "Common_code" just a collection of useful code files, which we're likely to re-use in different projects.
(If we have a module or class which is re-used in various projects, then we often keep a single master copy in 'common-code', and link to it.)
Visual Studio has no problem with this.
When I add any of the actual projects to subversion, all of their own files are added just fine, but the linked files are ignored.
(And as a consequence, if I then get a working copy of those files, then it's just the project files which get handled, I won't get a copy of the linked files.)
If I right click on any of the linked files, I the only subversion options I get are to refresh their status or to select the working folder.
I was wondering what the correct way to handle this situation was ?
Any advice would be much appreciated
Thanks !
Robert
if I understand your question correctly then I think SVN is acting in the desired way. A linked file is merely a reference to another file. That reference exists only in the .csproj file which is checked in. It would not make sense to have two copies of the same file in source control, and it could lead to versioning issues. The first time you checkout your repository doing a build on your projects should copy the files from Common_code to the places that they're linked.
As an aside we've had alot of random issues with .csproj linked files and SVN, and so try to avoid linked files where possible. A better way to re-use files across projects is obviously just to embed them in a library and then reference that library. This should work fine with the exception of certain files like Javascript/CSS.
Also you may want to check out SVN externals, a workmate mentioned this can be used to share common libraries between multiple projects, although as a disclaimer I haven't tried this myself and can't comment on the merits or drawbacks of the approach.
Thanks for the advice, I actually did something similar to your suggestion.
I didn't want to make a full blown library, but I did make up a dummy project, and put my shared files into that.
Then I added the dummy project to the repository.
AhnkSvn now seems to be satisfied that the linked files are under subversion control, and seems to handle them just fine.
(I haven't added any reference to the dummy project to my existing projects - they just use the linked files as before - but now AhnkSvn shows me their status, and allows me to get the latest version, and commit changes.)
I can see the case for having a proper library - but that would have meant modifying a large body of existing projects. This approach lets me get up and running with Subversion without requiring those changes first.
That the logical follow-up for the my previous question: "How to check all projects in solution for some criteria?"
I was given quite a good answer to use CustomAfterMicrosoftCommonTargets, CustomBeforeMicrosoftCommonTargets. They do work, so I decided not to stop in the middle.
Issue is that I don't want machine-wide tasks. It's not a good idea neither for me (it will affect other builds. sure, this can be handled, but still), nor for my teammates (I don't want to let them put something in system folders... ), nor for build server.
What is needed: solution to be built from scratch out of source control on clean machine with either Visual Studio or MSBuild.
It appeared that Custom*MicrosoftCommonTargets are regular properties.
So, how to specify this property? It works pretty fine when to set it from command line.
That's strange, but it appears that bit of magic present here: property passed as command line parameter to one build is transitively passed to all nested builds!
That's fine for build server. But this won't work with Visual Studio build. And even declaring solution-level property won't help: neither static, nor dynamic properties are transfer to nested builds.
...I have a hacky idea to set environment variable on before solution build and erase it on after. But I don't like it. Any better ideas?
I use a bit different technique then #Spider M9. I want that all projects in solution tree/all subdirectories from current directory use extended build throw Custom*MicrosoftCommonTargets. I don't like to be forced to change every new project to import custom targets/props.
I place special file, let's say msbuild.include, in the root directory and my custom targets loader for every project tries to find it in ., ..\, ..\..\, and so on. msbuild.include contains flags that triggers execution of custom actions. If loader can't find this file it disables loading all custom targets and stoppes. This gives me ability to use my build extensions with projects from work repositories and to not use with opensource projects.
If you are interested in I can publish loader. It's a pretty simple and elegant solution.
For example I can sign any assembly in all projects in all subfolders with my key.
I always set up every project to import a standard .props file. Use the GetDirectoryNameOfFileAbove property function (see MSDN) to find it. Do this as the first line of every project file. Once established, you can redirect from that file to other imports. Another trick is to have that standard import (that would obviously be under version control) import conditionally another .props file only if it exists. This optional file would not be in version control, but is available for any developer to create and modify with their own private/temporary properties or other behavior.
I have a couple of different solutions, in which some projects may depend on output from projects in other solutions. To manage this, I've been copying dll files from the /bin/ folder in each project to a shared library location after build, and then copy/reference them from there to the dependent project.
However, as the library solution gets larger, this tends to become unmaintainable. Too much of my time is being spent traversing solution directories in Windows Explorer looking for /bin/ folders, and trying to figure out which one, or which ones, of the dll files from each one I need.
Is there any way to give Visual Studio a hint that I want all projects in a solution to have the same output directory? For example, a /bin/ folder directly under the solution folder, where all projects put their output.
If possible, I'd like to achieve this without hard-coded post-build events that copy the files, since that will fail if a project output changes file name, or adds another file. I'd rather like to change the location of the actual output directory - the location of $(OutDir), if you will.
I know you said you don't want to use post build events, but your reason as to why not intrigued me. It sounds like you might be hard coding the name of the .dll in your post build event. That can easily be avoided.
xcopy "$(TargetDir)*" "c:\common\" /Y
The * would just cause everything in your bin/Debug/ folder to get copied to your common folder. You could also just copy dlls if you want. Or, if you use $(TargetPath), you'll copy just the 1 dll that is the result of the project, and not any other related dependencies.
UPDATE
The way we do it is each projects entire bin folder is copied to a subfolder. Suppose you have 2 projects, WebUtil and HtmlParser, where WebUtil depends on HtmlParser. For both projects, use xcopy "$(TargetDir)*" "c:\common\$(ProjectName)" /Y. This will create c:\common\WebUtil\ and c:\common\HtmlParser. In WebUtil, add a reference to c:\common\HtmlParser\HtmlParser.dll. There will now be 2 copies of HtmlParser.dll in c:\common.
c:\common\HtmlParser\HtmlParser.dll // the most recent build.
c:\common\WebUtil\HtmlParser // what was the most recent build when WebUtil was built
This has all kinds of advantages. If you change the API of HtmlParser, WebUtil will continue to work, since it will have the older HtmlParser.dll until you try to rebuild WebUtil (at which point you'll get build errors because of the changed API).
Now, if a 3rd project got in the mix that depended on WebUtil, and you're using some part of WebUtil that exposes classes in HtmlParser, then you'll need to add a reference to both projects from your new project. When you add a reference to HtmlParser.dll, use the one in c:\common\WebUtil. You do this because you're only including it as a necessary requirement of WebUtil. Now you'll always have the version of HtmlParser.dll that matches your current version of WebUtil.dll.
I hope that makes sense. It can definitely be a tricky thing to manage. Just wait till you have to start pulling down all your dependencies using svn:externals =P
You can set the output directory in each project properties.
Right click on the project, select Properties
For C#, it is one of the Build property page, under Output, Output directory.
In VB.Net projects, it is on the Compile tab, in the textbox at the top.