I've taken over maintenance of a web solution that contains a number of c# projects. On compilation one project creates the Roslyn bin folder under it's output directory bin folder as expected, another however creates the Roslyn bin folder in C:\bin\roslyn. I've searched the project files for any clues as to why this is happening but cannot find any reference to it. In the build log it shows that when the project that creates the Roslyn folder under it's output dir, the files are copied with an absolute destination path (C:\project output dir\bin\Roslyn), whereas in the project that creates it under C:\bin\Roslyn, the files are listed as just copied to \bin\Roslyn. Should a project setting point to the output directory ($(OutputDir) is set). Any pointers would be greatly appreciated.
I know of 3 ways to investigate MSBuild issues:
In general, I found binary logs (binlogs) to be most useful. Create one by passing the -bl, then open the generated file in the MSBuild Structured Log Viewer. It shows very similar data to the build log, but has a good search and shows what happened in a tree view that greatly helps understanding.
What I think will probably be of most use to you for this specific issue is outputting the pre-processed file by running msbuild -pp or dotnet msbuild -pp. This basically finds all MSBuild Import statements, and replaces them with the actual contents of the imported file. I believe that MSBuild always evaluates top-down. So if property1 is defined, then is used to evaluate property2, then later property1 changes, the value of property2 will retain its value from when it was evaluated. Keep in mind that execution of targets cause the target to be evaluated, so it can use a property that was defined further down the file, as long as the lower down target was run first, or the properties or items defined further down are global (not in a target).
Finally, if all else fails, you can try setting the log output to the highest verbosity, diagnostic. Note that msbuild -v:d is detailed verbosity, you need msbuild -v:diag to set diagnostic verbosity. I'm not sure if this actually outputs anything more than what's in the binlog, but I think there might have been one or two times I was desperate and the diag output helped (but I can't remember if I used the binlog in those occasions). Anyway, worth a try if the other two methods above don't help.
Thanks for the pointers, Icepickle is correct in questioning if there is a problem, the solution works but there is just a redundant folder placed off the root drive so it's not ideal. I have sorted the problem by going through the solution build (in Diagnostic Verbosity), and in all the projects that are copying files to the root folder I added the following in their project files. This points to the correct path and will only copy if the files aren't already there.
<Target Name="CopyRoslynCompilerFilesToOutputDirectory" AfterTargets="AfterBuild">
<ItemGroup>
<RoslynFiles Include="$(CscToolPath)\*" />
</ItemGroup>
<MakeDir Directories="$(SolutionDir)$(SolutionName)\bin\roslyn" />
<Copy SourceFiles="#(RoslynFiles)" DestinationFolder="$(SolutionDir)$(SolutionName)\bin\roslyn" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" />
Related
A common practice when staging unmanaged libraries or resources is to add those resources as links to the project and set them to copy to the output directory.
As of Visual Studio 2013, this is handled quite well and even if they are referenced Library Foo which is then consumed by Application Bar, they will end up in Bar's output directory.
The same doesn't appear to be true for directories of files though.
In this case, my application appears to only copy this directory of files to the output directory on rebuild only. Then inevitably I'll do a couple build actions and I'll notice the Libs directory is empty again. Then do a Rebuild and cef.pak is back.
My solution to this was to employ a Build Step to copy the Libs directory manually to the output directory. Though I cannot find the proper macro to generically express that even though this build step is part of Foo, I want the files copied to the Bar's (e.g. StartUp Project in VS terms) output directory.
Update
Thank you everyone for the great answers and testing this. I should clarify that I still see this issue when there is an extra level of library in between. That is to say Application Bar referencing library Foo which references library Other which is the one w/ these linked files. In that case, when Other's linked files are set to copy to output directory, they seem to only make it on rebuilds. My solution to this is less than ideal which is to have Foo reference Other directly.
unless you rebuild/clean solution, VS does remove files from output folder.
so i believe you forgot to say that your program(or third party) does so.
First: this is an example for similar/exact bug.
download the solution and do the following steps:
rebuild the solution
open the output folder of Bar
the file cef.pak will be exist in Libs
press F5 -> execute Bar -> everything alright
execute again with F5 -> you will receive an file not found exception
if you repeat steps 1-5 you will receive the same behavior
The issue exist only if there is no changes in Bar and something(the exe / other process/etc) has deleted the file.
Second: I've already faced this issue.
when one of my team member reported this issue, then the answer he received was a kind of "it's not a bug it's a feature"....
As i know, there is no "magic button" to solve for this problem.
But there are several workarounds:
(as you did) adding pre/post build events. I recommend you to do this in new build configuration.
change the build output folder of Foo to Bar output folder.
add in your code a path replacement with if debug:
#if DEBUG
cefFilePath = <build output of foo>
#endif
put the file as embedded resource then extract it.
create an extension\external program to deal with this problem. each project the file keep the information about those file:
<ItemGroup>
<None Include="Libs\cef.pak">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
the application/plugin will do the job for you.
I can offer more solutions/workarounds, but I believe that you did the right thing -> build event.
edit:
I updated my example in the link. now the solution contains 3 workarounds example.
Tried doing same with VS2013. It worked quite well, everytime I change something in Bar's File, Run Foo (Startup Project), the latest file is copied there. I am just writing steps I followed, may be I am missing something to replicate or you missed a step. Please have a look:
Create two Projects Foo and Bar
Created a directory Libs in Bar
Added a text file "TextFile1.txt" in Libs
Right Click -> Properties of the file -> Copy to Output Directory = Copy Always
Added reference of Bar from Foo Project
Started reading the TextFile's text from Foo's Main method. Reading file from Bin\Debug\Libs directory.
Every time I press F5 I see the latest file get copied there.
Edit "Foo.csproj" (make sure the project is closed in VS) and add this add the bottom, in place of a commented out example that is already there in every new project file (use your own paths):
<Target Name="AfterBuild">
<Copy SourceFiles="Libs\cef.pak;Libs\file2.ext" DestinationFolder="..\..\Bar Solution\Bar Project\output" SkipUnchangedFiles="True" OverwriteReadOnlyFiles="True" />
</Target>
Edit "Bar.csproj" as well, just in case changing those files doesn't trigger Foo build:
<Target Name="BeforeBuild">
<Copy SourceFiles="..\..\Foo Solution\Foo Project\Libs\cef.pak;..\..\Foo Solution\Foo Project\Libs\file2.ext" DestinationFolder="output" SkipUnchangedFiles="True" OverwriteReadOnlyFiles="True" />
</Target>
After changing something in my C# code, I can run it without problem. However, if I run the .exe file in WindowsFormsApplication1/bin/Debug/, I would get an error message. By checking the 'Date modified' of this .exe file, I found it wouldn't update accordingly.
I then tried to do Build/Build Solution and Rebuild Solution but the .exe file would still not be updated.
Can you tell me how to update the .exe file?
To view the output directory for your builds, right-click on the project you are trying to build and select "Properties". Under the "Build" tab you should find the following:
At the top you can also see the configuration under which your project is building (usually Debug or Release). By default, the output directory for Debug and Release builds are, respectively, "bin\Debug" and "bin\Release". EDIT: To change which build configuration is currently active, you will need to go to Build -> Configuration Manager:
Once you have checked the build directory paths and the configuration which you are building, do a Clean Solution followed by a Build Solution and your *.exe file should now be the latest version.
Hope this helps!
Alex
EDIT:
Apologies - meant also to mention that the build directory paths are relative to the project directory (the directory where the *.csproj file for your project lives). So updating the *.exe file in WindowsFormsApplication1\bin\Debug under Debug configuration with the default output directory is contingent upon WindowsFormsApplication1.csproj being contained in the WindowsFormsApplication1 directory.
Somehow I figured out why it is happening. If you have multiple projects in a solution (I assume that's the case above), check the status of "Build" checkbox in the property pages. I faced same issue when one of my three projects under solution was not refreshing whenever I rebuild the solution.
Hope this will help!
I'm tidying my projects. And I found the way to remove the object folder with adding:
%TEMP%
In my projects. But I want somehow to make this global setting or to auto delete my obj dirs after a build. Is there a way to do that?
I personally like having a specific Output folder in my project where I put all the compiled files.
I have the following command line in the Post-build events.
copy "$(TargetPath)" "$(SolutionDir)\Output\$(TargetFileName)"
This will copy the compiled file to the Output directory inside the Solution. You would need to add this to all the projects in your solution.
If you have any dependencies that also needs to be copied you could add something like this as well.
copy "$(ProjectDir)Dependencies\Language.xml" "$(SolutionDir)\Output\Extensions\Language.xml"
[EDIT]
You can try the following to have the file copied first, and then once that is done delete the object folder.
copy "$(TargetPath)" "$(SolutionDir)\Output\$(TargetFileName)"
rd /s /q "$(ProjectDir)\obj"
[EDIT2] Updated with screenshots to illustrate. :)
This is how my object folder normally would look like after compiling the project.
This is how it looks after compiling it with the above command. As you can see the folder is re-created after the event by Visual Studio, but the folder is empty.
You might want to double check that you are running Visual Studio with elevated permissions. To do so, simply right click on the Visual Studio and choose "Run as Administrator".
Are you using source control?
This comment sounds like you don't:
While archiving, those are unneeded megabytes.
("Archiving" sounds a bit like copying the whole project folder regularly to something like backup_yyyymmdd)
If you're not using source control, you should definitively consider starting to use it.
Apart from the general advantages (like, having a change history with dates and comments...), it has an out-of-the-box solution for your problem with the obj folders:
Every good source control software out there supports ignoring certain files or folders which you can define (ignoring means: they can never be committed to the source repository, you don't even see them in the list of changed files, not even when they were changed).
For example, in Mercurial (which I use) the ignore settings are saved in a file named .hgignore in the main folder (Git has the same, it's just called .gitignore).
My default .hgignore file for all Visual Studio projects looks like this:
syntax: glob
bin
obj
*.suo
*.user
The first line belongs to Mercurial's ignore syntax, the rest are the settings what to ignore.
You can see that the bin and obj folders are ignored...and they are ignored no matter in which subfolder they are!
So I don't have to care about where the obj folders actually are, and I don't have to delete them manually every time I build my solution. They are simply non-existent in my source control history.
Plus, I have a variation of Fuji's answer about putting everything in one single output folder:
I like to do this as well, but I prefer changing the output folders in Visual Studio's project settings instead of using post-build events.
The default output folders are:
bin\Debug\
bin\Release\
I change them to:
..\build\Debug\
..\build\Release\
This compiles everything into subfolders of a build folder which is at the same level like the .sln file (which means: all projects in the solution directly compile into the same folder).
It also reduces compile time because Visual Studio won't have to copy all the dependencies after compiling (because everything already is in the same folder).
(I do it mainly because of the compile time, because I ignore the bin and obj folders anyway in Mercurial as described above, so I don't care where they actually are)
For some reason, we have a script that creates batch files to XCOPY our compiled assemblies, config files, and various other files to a network share for our beta testers. We do have an installer, but some don't have the permissions required to run the installer, or they're running over Citrix.
If you vomited all over your desk at the mentions of XCOPY and Citrix, use it as an excuse to go home early. You're welcome.
The code currently has hundreds of lines like:
CreateScripts(basePath, "Client", outputDir, FileType.EXE | FileType.DLL | FileType.XML | FileType.CONFIG);
It used to be worse, with 20 int parameters (one per file type) representing whether or not to copy that file type to the output directory.
These hundreds of lines create upload/download batch files with thousands of XCOPY lines. In our setup projects, we can reference things like "Primary output from Client" and "Content Files from Client". I'd love to be able to do that programmatically from a non-setup project, but I'm at a loss.
Obviously MS does it, either using an API or by parsing the .csproj files. How would I go about doing this? I'm just looking for a way to get a list of files for any of the setup categories, i.e.:
Primary Output
Localized Resources
Content Files
Documentation Files
EDIT:
I have a setup project like Hath suggested, and it's halfway to what I'm looking for. The only problem keeping that from being a perfect solution is that multiple projects depend on the same assemblies being in their own folder, and the setup will only copy the file once.
Example:
Projects Admin, Client, and Server all rely on ExceptionHandler.dll, and Admin and Client both rely on Util.dll, while Server does not. This is what I'm looking for:
Admin
Admin.exe
Admin.exe.config
ExceptionHandler.dll
Util.dll
Client
Client.exe
Client.exe.config
ExceptionHandler.dll
Util.dll
Server
Server.exe
Server.exe.config
ExceptionHandler.dll
Since the referenced assemblies are all the same, what I get is this:
Admin
Admin.exe
Admin.exe.config
ExceptionHandler.dll
Util.dll
Client
Client.exe
Client.exe.config
Server
Server.exe
Server.exe.config
This causes a FileNotFoundException when either Client or Server can't find one of the two DLLs it's expecting.
Is there a setup property I'm missing to make it always copy the output, even if it's duplicated elsewhere in another project's output?
EDIT AGAIN: All referenced DLLs are set to "Copy Local", and always have been. I found a decent article on using NAnt and XSLT to grab the list of files, so that may be a possible solution as well, as neouser99 suggested.
ACCEPTED SOLUTION: I'm pretty much back where I started. All .exe and .dll outputs are put into a "bin" directory in the setup project, loosely packed. The other per-application folders contain shortcuts to the executable in that directory.
The difference now is, I'm going to add a custom action to the installer to use reflection, enumerate the dependencies for each executable output, and copy the .exe and .dll files to the separate directories. Bit of a pain, as I just assumed there was a way to programmatically detect what files would be included via some setup library.
why not use another setup project and just set the 'Package files' setting to As Loose uncompressed files (setup project->properties)? then share the folder.. or something.
edit:
I see, you have 3 folders for your outputs. but the setup project only detects the ExceptionHandler.dll and Util.dll once, so it will just pick the first folder and put it in there.
You could do a setup project for each project - bit annoying maybe..
You could manually add in the dll's to the projects that are missing the assembly's
either by adding in the File by 'add file' or 'add assembly' or 'add project output' if you have those projects in the same solution.. (I doubt that's the case though).
or just dump all of them into one output directory...
Although it's designed as a build tool, you might find NAnt to be extremely useful in what you are talking about. The tasks (build, copy, move, delete, etc.) that you can define allow for very fine-grained file lookups, up to general, full folders. If you also incorporate NAnt into your build process, I think you could find that it helps out in more ways then one.
Another approach that has worked for me in the past is to add the shared resource (Assembly, DLL or project) as a reference to each of the Admin, Server and Client projects. Then open the properties panel for the referenced item in each project and set "Copy Local" to true.
Now when you build the projects, each will have its own instance of the Assembly copied into its output folder.
This should also cause the shared components added in this manner to be replicated in each of the output folders in the setup package.
A completely different approach could be to set them up as symbolic links on the network share. A symbolic link is basically a short-cut where the file-system hides the fact that it is a short-cut, so all other applications actually believes that the file has been copied (http://en.wikipedia.org/wiki/NTFS_symbolic_link).
One advantage of this approach is that the file is updated immediately as the file changes and not only when you build your projects. So when you for instance save one of the config-files with a text-editor the update is applied immediately.
The following MSBuild script part can build your SLN file (you can replace it with .csproj) and will report a list of all projects that were build (Dlls, EXEs).
<MSBuild Projects="MySolution.sln" Targets="Clean; Rebuild" Properties="Configuration=$(BuildMode);">
<Output TaskParameter="TargetOutputs"
ItemName="AssembliesBuilt" />
</MSBuild>
Now, this doesn't really solve your problem, but it gets you a list of everything that was build. You also have copylocal, so you could probably just take AssembiesBuild and copy all DLL and .CONFIG files from there.
Example:
AssembliesBuild = c:\myproj\something1\build.dll
you'd go to c:\myproj\something1\ and simply search for all *.dll and *.config files and include them. You can do this pretty easily with MSBuild or powershell, if you have it installed. To output a XCOPY script from MSBuild, I think you'll need MSBuild contrib projct installed.
I have a solution with many projects. There is actually a Core project and a few plugins. I changed OutputPath for all plugins so all binaries end up in the Core bin\debug folder. (this is necessary as the Core do not have a reference on plugins, hence it does not "include" plugins binaries when it is compiled.)
So basically my folder structure is as follow:
Solution
MySolution.sln
Plugin1\
Plugin2\
Core\bin\debug
Each plugin OutputPath is "..\Core\bin\debug". When I open the solution Visual Studio creates a folder "Core\bin\debug" in Solution's folder parent as if the relative path starts from .sln file. However when I build the solution the binaries are output to the correct path ("Solution\Core\bin\debug").
Core\bin\debug
It looks like a Visual Studio bug to me, but maybe I overlooked some option somewhere. Any ideas how to resolve this problem ?
PS: I know this not a critical issue as everything build and works fine, however I dislike the idea of meaningless folder hanging around
Rather than changing the output location of the plug-ins, what you could do is create a post-build script (Properties \ Build Events tab) for them that will copy the them to the Core folder. That would prevent the confusion with output folders.
This command line should do the trick for you:
copy "$(TargetPath)" "$(SolutionDir)Core\$(OutDir)"
If you need to copy .pdb and .config files as well, you can add more lines:
copy "$(TargetPath).pdb" "$(SolutionDir)Core\$(OutDir)"
copy "$(TargetPath).config" "$(SolutionDir)Core\$(OutDir)"
If you really want to do it with a single line, this should also work, though it's not as clean:
copy "$(TargetPath)*" "$(SolutionDir)Core\$(OutDir)"
If you're not using the same output path in both the main project and the add-ons, you'll need to replace $(OutDir) with a hard-coded value. If you have them set to target the typical "\bin\Debug" folder (or have just left the defaults in place), then you can get away with using the $(OutDir) value.
Instead of using "..\Core\bin\debug", use "$(SolutionDir)\Core\bin\debug".