I'm trying to write a code generation tool. For this tool it's important that the generated code is available prior to building (i.e., for IntelliSense). I know Visual Studio will at least partially evaluate the project build plan automatically to generate IntelliSense, but I can't find much information on the details.
As a simpler example, let's say I want to take all items with build action None and compile them. I have a project like this:
<Project [...]>
[...]
<Compile Include="Foo.cs" />
<None Include="Bar.cs" />
</Project>
One way to get Bar.cs to compile is to add the following to the project:
<PropertyGroup>
<CoreCompileDependsOn>
$(CoreCompileDependsOn);IndirectCompile
</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="IndirectCompile">
<CreateItem Include="#(None)">
<Output ItemName="Compile" TaskParameter="Include" />
</CreateItem>
</Target>
If I do it this way, Visual Studio acts basically the same as if Bar.cs had the Compile action to begin with. IntelliSense is fully available; if I make a change in Bar.cs it's reflected immediately (well, as immediate as the background operation normally is) in IntelliSense when I'm editing Foo.cs, and so on.
However, say instead of directly compiling the None entry, I want to copy it to the obj directory and then compile it from there. I can do this by changing the IndirectCompile target to this:
<Target Name="IndirectCompile"
Inputs="#(None)"
Outputs="#(None->'$(IntermediateOutputPath)%(FileName).g.cs')"
>
<Copy SourceFiles="#(None)"
DestinationFiles="#(None->'$(IntermediateOutputPath)%(FileName).g.cs')"
>
<Output TaskParameter="DestinationFiles" ItemName="Compile" />
</Copy>
</Target>
Doing this causes IntelliSense to stop updating. The task works on build, dependency analysis and incremental building work, Visual Studio just stops automatically running it when an input file is saved.
So, that leads to the title question: How does Visual Studio choose to run targets or not for IntelliSense? The only official documentation I've found has been this, specifically the "Design-Time IntelliSense" section. I'm pretty sure my code meets all those criteria. What am I missing?
After a few days of experimenting and poking around in the debugger I think I have found the answer, and unfortunately that answer is that this is not possible (at least not in a clearly supported way -- I'm sure there are ways to trick the system).
When a project is loaded, and when the project itself changes (files added/removed, build actions changed, etc), the IntelliSense build is executed (csproj.dll!CLangCompiler::RunIntellisenseBuild). This build will run tasks up to and including the Csc task. Csc will not execute normally, but instead just feed its inputs back into its host (Visual Studio).
From this point on, Visual Studio keeps track of the files that were given as Sources to the Csc task. It will monitor those files for changes, and when they change, update IntelliSense. So in my example, if I manually edit Bar.g.cs those changes will be picked up. But the build tasks themselves will not be run again until the project changes or a build is explicitly requested.
So, that's disappointing, but not surprising, I guess. It also explains something else I had always wondered about -- XAML files with a code-behind tend to have a Custom Tool action of MSBuild:Compile, presumably for exactly this reason.
I'm going to mark this as the answer, but I'd love to be told I'm wrong and that I missed something.
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.
this is my situation:
I have VS2010 solution with X projects included.
Wix project that can create msi from all compiled artifacts.
I have build machine \ Jenkins that first compile (MSBuild .Net 4) all the solution, then compile the wix to package it to msi.
What\how can I inject to all artifacts\dlls the number of the product (e.g 11.2.0.4789) - as simple as possible?
Is there and command line arguments that can be passed while compiling the solution?
There are tools, such as several extensions for MSBuild, that do version stamping but each assumes a particular workflow. You might find one that works for you but a DIY method would help you evaluate them, even if it isn't your final solution.
You can add a property to the MSBuild command-line like this:
msbuild /p:VersionStamp=11.2.0.4789
Note: I assume you are going to parameterize the Jenkins build in some way or generate the number during a preceding build step. Here is a simulation of that:
echo 11.2.0.4789 >version.txt
set /p version=reading from pipe <version.txt
msbuild /p:VersionStamp=%version%
Now, the work is in getting each project to use it. That would depend on the project type and where you want VersionStamp to appear.
For a csproj, you might want to use it as the AssemblyVersion. The simplest way is to move the attribute to a cs file by itself and rewrite it every time. I would leave a comment in AssemblyInfo.cs as a clue to where it now comes from. You can include the cs file in your project either dynamically or permanently. I prefer dynamically since it is effectively an intermediate file for the build. So, in your .csproj add the following in a text editor (e.g. Visual Studio. Unload and Edit project):
<Target Name="BeforeBuild">
<PropertyGroup>
<AssemblyVersionPath>$(IntermediateOutputDir)AssemblyVersion.cs</AssemblyVersionPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(AssemblyVersionPath)" />
</ItemGroup>
<WriteLinesToFile
File='$(AssemblyVersionPath)'
Overwrite="true"
Condition="'$(ProductVersion)' != ''"
Lines='using System.Reflection%3b;
[assembly: AssemblyVersion("$(VersionStamp)")]' />
</Target>
This is sufficient but a more thorough solution would include adding the file to a list so it is cleaned with other files and only writing the file if the version changed to prevent unnecessary rebuilds, etc.
Use a similar technique for other project types.
What we currently have:
SVN as source repository
MSBuild scripts for building and setting version numbers
This version numbers is a combination, properties in the build file, and the latest revision from SVN
Cruise control to run the build scripts
What I would like to do:
We have our build scripts updating the version number each time we do the build. I would like to do that automatically each time we do a build by pressing F5 within visual studio. So update the GlobalAssemblyInfo.cs with the correct version number, from the revision number in SVN
A better way to do this is with a SVN hook. The version number would only be changed on a commit, so you can write a hook that updates that file as part of the commit.
Or check out this: http://www.codeproject.com/KB/architecture/svn_visual_studio.aspx
I was just reading about this yesterday with regards to builds from TFS 2010. I know it isn't completely applicable to your situation or architecture, but he raises some good points about how to go about your assembly versioning.
More specifically, the author references this post. Again, it is specific to MSBuild, but there are some points that are applicable to assembly versioning in general
Edit:
The first link talks about the pros and cons of various approaches including using the date as part of your version number (not particularly useful), but leans towards including the TFS build number as part of the assembly version. He refers to his preferred versioning scheme which allows him to identify quickly and easily what the build relates to (hotfix etc). This link is mainly useful to give you some alternative views on assembly versioning.
The second link is referred to by the first link and has some code examples on how he uses MSBuild to help version his assemblies. This could also give some useful ideas on how to go about it. Maybe combine some of these with a post-build event to achieve your goal.
I found a way to do it with the F5 by modifying the project file, and using the MSBUILD.Community.Tasks
<Target Name="BeforeBuild">
<MSBuild.Community.Tasks.Subversion.SvnInfo Username="$(SvnUserName)" Password="$(SvnPassword)" RepositoryPath="$(SvnPath)">
<Output TaskParameter="Revision" PropertyName="SvnRevision" />
</MSBuild.Community.Tasks.Subversion.SvnInfo>
<CreateProperty Value="$(Major).$(Minor).$(Build).$(SvnRevision)">
<Output TaskParameter="Value" PropertyName="FullVersionNumber" />
</CreateProperty>
<MSBuild.Community.Tasks.AssemblyInfo
OutputFile="$(ProjectFolder)\GlobalAssemblyInfo.cs"
CodeLanguage="CS"
AssemblyVersion="$(FullVersionNumber)"
AssemblyFileVersion="$(FullVersionNumber)" />
<Message Text="Test"/>
</Target>
In Visual Studio 2010 we have under 'tools|options|projects and solutions|build and run' (couldn't find a correct image on the internet) two options for the logging of MSBuild:
'MSBuild project build output verbosity' and
'MSBuild project build log verbosity'.
So I was hoping to be able to get a minimal build log in the output view within Visual Studio devenv (correct) while at the same time a detailed build log in some log file.
I cannot find a way to configure a build log file to appear.
Note: I do not want to configure my own MSBuild.
Log file from Visual Studio is only supported for C++ projects. You just have to work with the output window for others.
http://msdn.microsoft.com/en-us/library/b0bktkzs.aspx says:
Examine the build log in the intermediate files directory to see what actually executed. The path and name of the build log is represented by the MSBuild macro expression, $(IntDir)\$(MSBuildProjectName).log.
[And the easiest way to get there is to do Project|Show all files, then go to Solution Explorer and right click to Open Folder in Windows Explorer]
EDIT: To appease our disgruntled -1er... You could obviously infer from this that you could add a <Execute Command="notepad.exe $(IntDir)\$(MSBuildProjectName).log"/> or similar if it needs to literally pop up, but that doesnt make sense to me.
EDIT 2: EXAMPLE. Edit the .csproj file, and in the section with
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
-->
Change it to:
<Target Name="AfterBuild">
<Exec Command="notepad.exe $(IntDir)\$(MSBuildProjectName).log" />
</Target>
Reason I didnt expand it out is that this would get annoying quick. You could potentially put:
notepad.exe $(IntDir)\$(MSBuildProjectName).log
In your Post Build step. This would work slightly better as it would only fire when the compile has actually done something.
BTW highly recommend getting the Hashimi book - it makes all this stuff obvious and makes you give answers that assume its straightforward :P
I have some content files that I would like to share between a number of projects in Visual Studio.
I have put these files in their own project, set the build action to "Content", and the copy to output directory to "Copy if newer". I would like all these files to be copied to the bin/debug directory of the projects that reference them.
I can get it to work by including a reference to the "contents" project in each of the projects that need the files, but that requires that a minimal assembly be generated (3K). I assume there is a way, using MSBuild, to make this all work without creating the empty assembly?
Thanks to everone who took the time to make a suggestion about how to solve this problem.
It turns out that if I want my compiled content files to be treated like content files (in that they get copied to the output directory of any other project that references my project), I need to create a target which runs before GetCopyToOutputDirectoryItems, and add the full path of the compiled content files to the AllItemsFullPathWithTargetPath ItemGroup. MSBuild calls GetCopyToOutputDirectoryItems for projects on which the current project depends, and uses the resulting file list to determine the files that are copied along with the assembly.dll. Here is the XML from my .csproj, just in case someone else has a similar problem.
I have a custom task called "ZipDictionary", and I accumulate all the files that I am going to compile in an ItemGroup called DictionaryCompile. My target, "FixGetCopyToOutputDirectoryItems" is executed before "GetCopyToOutputDirectoryItems". I don't do the actual compilation there, since this target can be called multiple times by referencing projects, and it would hurt performance. The target does some transforms to get the post-compilation file names, and then returns the full paths to all the files, since relative paths will not work when copy is called from the referencing project.
<ItemGroup>
<DictionaryCompile Include="Dictionaries\it-IT.dic">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</DictionaryCompile>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<UsingTask TaskName="ZipDictionary" AssemblyFile="..\LogicTree.DictionaryCompiler\bin\Debug\LogicTree.DictionaryCompiler.dll"/>
<Target Name="BeforeCompile">
<Message Text="Files #(DictionaryCompile)" Importance="high" />
<ZipDictionary DictionaryFiles="#(DictionaryCompile)" OutputDirectory="$(OutputPath)">
<Output TaskParameter="OutputFiles" ItemName="DictionaryOutputFiles" />
</ZipDictionary>
</Target>
<Target Name="FixGetCopyToOutputDirectoryItems" BeforeTargets="GetCopyToOutputDirectoryItems">
<ItemGroup>
<_DictionaryCompile Include="#(DictionaryCompile->'$(OutputPath)Dictionaries\%(FileName).ltdic')" />
</ItemGroup>
<AssignTargetPath Files="#(_DictionaryCompile)" RootFolder="$(MSBuildProjectDirectory)\$(OutputPath)">
<Output TaskParameter="AssignedFiles" ItemName="_DictionaryCompileWithTargetPath" />
</AssignTargetPath>
<ItemGroup>
<AllItemsFullPathWithTargetPath Include="#(_DictionaryCompileWithTargetPath->'%(FullPath)')" Condition="'%(_DictionaryCompileWithTargetPath.CopyToOutputDirectory)'=='Always' or '%(_DictionaryCompileWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'" />
<_SourceItemsToCopyToOutputDirectoryAlways Include="#(_DictionaryCompileWithTargetPath->'%(FullPath)')" Condition="'%(_DictionaryCompileWithTargetPath.CopyToOutputDirectory)'=='Always'" />
<_SourceItemsToCopyToOutputDirectory Include="#(_DictionaryCompileWithTargetPath->'%(FullPath)')" Condition="'%(_DictionaryCompileWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'" />
</ItemGroup>
</Target>
A better possible solution would be to
place a common directory in the solution dir and place your common content files there.
in VS, in each project that should share this content, right-click add existing item, browse to the desired item(s), select, click the down-arrow on the add button and select add as link. In the project, you will notice the files are added with a 'shortcut' overlay.
In the project, select the newly added links and right-click->properties and select Build Action: content, Copy To Output Directory: Copy Always.
This is a simple solution to the problem given.
I use this technique for things like SQL scripts and partial config files (using configSource) with great success. This allows me to make changes to these files in a single location with the assurance that they will be propigated throughout the solution.
A more robust solution would be to create a project with embedded resources. This requires a bit more work to manage the content on the receiving end but may be worth it in the long run as having a bunch of loose artifacts flying about can become problematic.
Hope that helps.
A similar solution like the one Sky suggested can be found in my answer to "Is there a way to automatically include content files into asp.net project file?".
It allows to share your content but you must not touch the folder or its content inside VS because this breaks the recursive path.
This approach works best for auto-generated content - you don't have to bother about including new content files to your solution.
And of course you can reuse this in multiple solutions/projects.
We do something similar where we have "...ReleaseBuilds" that reference dlls and content we require for specific projects. Compiling copies everything to the bin debug folder and indeed creates the empty assembly.
Within Visual Studio we have a post-build event in the "...RealeaseBuild" (in project properties) that copies/deletes or run batch files to make sure we have all the files (configs, services etc etc) required and to delete the empty assembly.
HTH