Copy files preserving directory structure with rake - ruby

My goal is to copy a set of files specified by a pattern to the target dir. The files in source directory can have subdirs.
I tried:
cp_r(Dir.glob('**/*.html'), #target_dir):
and
cp_r(FileList['**/*.html'], #target_dir):
but neither work.
it only works when I do something like:
cp_r(Dir['.'], #target_dir):
But I need to copy only *.html files not anything else.
I need what
cp --parents
Command does
Any advice using existing Ruby/Rake methods?
UPDATE Looks like thing which is easier to do with Ant, is not possible with Ruby/Rake stack - may be I would need to look into something else. I don't want to write custom code to make it work in Ruby. I just thought about Ruby/Rake as appropriate solution for that.
UPDATE 2 This is how I do it with Ant
<target name="buildeweb" description="Builds web site" depends="clean">
<mkdir dir="${build.dir.web}" />
<copy todir="${build.dir.web}" verbose="true">
<fileset dir="${source.dir.web}">
<include name="**/*.html" />
<include name="**/*.htm" />
</fileset>
</copy>
<chmod perm="a+x">
<fileset dir="${build.dir.web}">
<include name="**/*.html" />
<include name="**/*.htm" />
</fileset>
</chmod>
</target>

If you want pure Ruby, you can do this (with a little help from FileUtils in the standard library).
require 'fileutils'
Dir.glob('**/*.html').each do |file|
dir, filename = File.dirname(file), File.basename(file)
dest = File.join(#target_dir, dir)
FileUtils.mkdir_p(dest)
FileUtils.copy_file(file, File.join(dest,filename))
end

I haven't heard of cp --parents, but if it does what you want then there is no shame in just using it from your Rakefile, like this:
system("cp --parents #{your} #{args}")

This could be useful:
# copy "files" to "dest" with any sub-folders after "src_root".
def copy_and_preserve files, dest, src_root
files.each {|f|
f.slice! src_root # the files without src_root dir
dest_dir = File.dirname(File.join(dest, f))
FileUtils.mkdir_p dest_dir # make dest dir
FileUtils.cp(File.join(src_root, f), dest_dir, {:verbose => true})
}
end

Related

How to use an msbuild CopyTask to copy a list of directories recursively

I would like to copy a list of directories recursively using a CopyTask.
The list is defined by a macro like so;
<ConanBinaryDirectories>some/path/;another/path/;</ConanBinaryDirectories>
I know a CopyTask can copy a single directory recursively, but how to deal with the specified format.
The ConanBinaryDirectories seems to be a MSBuild Property. If so, I assume you can use Msbuild Property Functions to get the single path.
Something like this:
<PropertyGroup>
<ConanBinaryDirectories>C:\Users\xxx\Desktop\Path1;C:\Users\xxx\Desktop\Path2;</ConanBinaryDirectories>
</PropertyGroup>
<PropertyGroup>
<SourcePath1>$(ConanBinaryDirectories.Split(";")[0])</SourcePath1> //C:\Users\xxx\Desktop\Path1
<SourcePath2>$(ConanBinaryDirectories.Split(";")[1])</SourcePath2> //C:\Users\xxx\Desktop\Path2
</PropertyGroup>
After you get the property which represents the single directory, you can use either 1.Copy task or 2.Exec task with xcopy command in it like this to copy the single directory to destination path.
All you need to do is to call the corresponding task twice in your custom target.
I know maybe what you want when you ask this question is a way like turn the MSBuild property to an MSBuild Item as the input of a task and do the copy job. But after my check: 1. The msbuild copy task actually doesn't support the input format like this some/path/ 2.We can use something like some/path/**/*.*, but it doesn't work well when our input could be something like #(...)/**/*.*.
So I suggest you split the macro to several paths and then use them into copy job.
Update:
The msbuild property doesn't support wildcard well. So to use something like **/*.*, you need to use Item instead of Property. You can have a look at this similar issue.
For a Property whose value is Path/*.*, it only represents a string Path/*.* most of the time while for an Item <MyItem Include="Path/*.*"/>, it represents all the files in the specified path. So no matter which way(copy task or xcopy command) we choose to do the copy job,the input should be an Item.
The script which works after test:
<PropertyGroup>
C:\Users\xxx\Desktop\Path1;C:\Users\xxx\Desktop\Path2
<PropertyGroup>
<SourcePath1>$(ConanBinaryDirectories.Split(";")[0])</SourcePath1>
<SourcePath2>$(ConanBinaryDirectories.Split(";")[1])</SourcePath2>
</PropertyGroup>
<ItemGroup>
<MySourceFiles Include="$(SourcePath1)\**\*.*" />
<MySourceFiles Include="$(SourcePath2)\**\*.*" />
</ItemGroup>
<Target Name="TestItem" AfterTargets="build">
<Copy SourceFiles="#(MySourceFiles)" DestinationFolder="$(OutputPath)"/>
</Target>
$(OutputPath) for C#, $(OutDir) for C++.

How To Reference Yaml File Relative to Launch File?

I have a launch file that is loading a yaml file:
<launch>
<rosparam command="load file="filename.yaml" />
<node pkg="my_package" type="my_package_node" name="my_package_node" />
</launch>
The filename.yaml file cannot be found unless I put a complete path: "/home/username/blah/blah/blah". Seeing as this launch file is used on multiple machines by different users and locations for the repository, I obviously cannot hard-code the path. So now what? How do I reference it relative to the location of the launch file?
we can set environment variable PWD for the specified node, eg we'd like to pass a rviz configuration file to rviz, the relative path can be like this:
<arg name="rviz_file" value="$(eval env('PWD')+'/config/showme.rviz')"/>
<node name="rviz" pkg="rviz" type="rviz" args="-d $(arg rviz_file)" required="true" />
Best answer I could find myself was to use $(find package_name) as a starting point:
<launch>
<rosparam command="load file="$(find package_name)/../../yamlFolder/filename.yaml" />
<node pkg="my_package" type="my_package_node" name="my_package_node" />
</launch>
Seems kinda silly though. No reference to the path relative to the launch file itself? Would definitely like a better answer.
If You are using ROS Packages, Launch Folders Are in the root folder of Package So $(find package) are actually one folder before your launch file
If your yaml file is in a ROS package, then I think using the find substitution arg as you do in your answer is the cleanest route. If it's someplace else, I would suggest leveraging environment variables with something like
<launch>
<arg name="yaml_path" default="$(optenv YAML_PATH)"/>
<arg if="$(eval yaml_path == '')" name="yaml_file" value="$(env HOME)/some_folder/filename.yaml" />
<arg unless="$(eval yaml_path == '')" name="yaml_file" value="$(arg yaml_path)/filename.yaml"/>
<rosparam command="load" file="$(arg yaml_file)"/>
</launch>
This kind of gives the best of all worlds. If you've set YAML_PATH as an environment variable (e.g. export YAML_PATH="$HOME/some_path" from terminal or in your bashrc) then the path for yaml_file will use it. If not, optenv will evaluate to an empty string and the arg yaml_file will be set to some default path relative to your home folder. AND, if you have a user that doesn't want to mess with the environment variables at all, they can still pass in a path manually by calling it like
roslaunch my_launch_file.launch yaml_path:=/home/my_name/my_preferred_folder

Why is there a need for a separate item in my MSBuild file?

There are many articles (like this and this) that show how to add files to be published, and they all say to add something like this to the publish profile (.pubxml):
<Target Name="CustomCollectFiles">
<ItemGroup>
<_CustomFiles Include="..\Extra Files\**\*" />
<FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
<DestinationRelativePath>Extra Files\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
Why is there a need for the new _CustomFiles item? Why not simply <FilesForPackagingFromProject Include="..\Extra Files\**\*">? I tried it, and for some reason this causes every file in the project to end up in the deployed Extra Files folder. Can someone explain me this behaviour please?
Since you are asking about why this is required, I will have to dive deep into what this code means to explain what you're seeing. <Message /> is our friend!
The meaning of %
Let's first look at what % means by using it in a <Message> task:
<ItemGroup>
<_CustomFiles Include="..\Extra Files\**\*" />
</ItemGroup>
<Message Text="File: %(_CustomFiles.Identity)" />
When you run this, you'll get the following output:
File: ..\Extra Files\file1.txt
File: ..\Extra Files\file2.txt
File: ..\Extra Files\file3.txt
...
File: ..\Extra Files\etc.txt
Basically, the Message task runs once for each item in the item group, because we used %.
What's in the item group?
Let's take a peek at the item group before we even make any changes to it. When this task begins, FilesForPackagingFromProject already has all of the files in them, with various metadata properties, including DestinationRelativePath. Let's see it by adding just this to our task:
<Message Text="File: %(FilesForPackagingFromProject.Identity) -> %(FilesForPackagingFromProject.DestinationRelativePath)" />
This outputs:
File: ..\obj\TempBuildDir\PrecompiledApp.config -> PrecompiledApp.config
File: ..\obj\TempBuildDir\Web.config -> Web.config
File: ..\obj\TempBuildDir\App_Themes\theme.css -> App_Themes\theme.css
...
It's important to realise that this item group is not empty to begin with. You are trying to add items to it.
The working code
When you have sub-elements in an element that has %, they apply once to each iteration, so let's now look at the working code:
<FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
<DestinationRelativePath>Extra Files\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
<Message Text="File: %(FilesForPackagingFromProject.Identity) -> %(FilesForPackagingFromProject.DestinationRelativePath)" />
For each item in _CustomFiles, we include it into the FilesForPackagingFromProject item group and set the DestinationRelativePath metadata property to the appropriate RecursiveDir/Filename values - basically the ones that apply for the current element being looked at. Let's look at what this outputs:
File: ..\obj\TempBuildDir\PrecompiledApp.config -> PrecompiledApp.config
File: ..\obj\TempBuildDir\Web.config -> Web.config
File: ..\obj\TempBuildDir\App_Themes\theme.css -> App_Themes\theme.css
...
File: ..\Extra Files\file1.txt -> Extra Files\file1.txt
File: ..\Extra Files\file2.txt -> Extra Files\file2.txt
File: ..\Extra Files\file3.txt -> Extra Files\file3.txt
...
File: ..\Extra Files\etc.txt -> Extra Files\etc.txt
Including just a single file
If you wanted to include just a single file, you can do so as follows:
<FilesForPackagingFromProject Include="..\Extra Files\file1.txt">
<DestinationRelativePath>Extra Files\file1.txt</DestinationRelativePath>
</FilesForPackagingFromProject>
This has no % to expand anywhere, so it does exactly what you would expect: it includes a single file into the output.
The broken code
Now let's try to include a single file, but without hard-coding the path and instead using the % expression from the original code:
<FilesForPackagingFromProject Include="..\Extra Files\file1.txt">
<DestinationRelativePath>Extra Files\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
<Message Text="File: %(FilesForPackagingFromProject.Identity) -> %(FilesForPackagingFromProject.DestinationRelativePath)" />
There are % here so things get expanded, but because this doesn't have a % in the item group element, the expansion works differently and things get pear-shaped:
File: ..\obj\TempBuildDir\PrecompiledApp.config -> PrecompiledApp.config
File: ..\obj\TempBuildDir\Web.config -> Web.config
File: ..\obj\TempBuildDir\App_Themes\theme.css -> App_Themes\theme.css
...
File: ..\Extra Files\file1.txt -> Extra Files\PrecompiledApp.config
File: ..\Extra Files\file1.txt -> Extra Files\Web.config
File: ..\Extra Files\file1.txt -> Extra Files\theme.css
So instead of adding file1.txt to the item group once, it iterates over the entire collection and adds file1.txt once for each file already in it. RecursiveDir is not set in this context, while Filename/Extension are the original filename of each file in the group.
Hopefully you can see now that this will create a file for each file in your entire deployment, but in a flat tree, and notably, the contents will be that of file1.txt rather than the original file.
When you include a wildcard instead of just one file, the same thing happens for every file matched by the wildcard.
How to fix this
Stick with the %(_CustomFiles) fix. Hopefully you will now see why it's necessary and how it does what it does. I do believe this is how you are supposed to do this: here's another question about it, with an answer that recommends this approach.

Change some fields in a file

I need to analyses a file and change some fields.
Example:
<taskdef uri="xxxxxx" resource="/mnt/data/yyy.xml">
<classpath path="/mnt/data/test.jar"/>
</taskdef>
<target name="test"
description="this is a xml file">
<fileset dir="/tmp/data/output/test_1/" includes=all/>
In my case I need to find this part:
<fileset dir="/tmp/data/output/test_1/" includes=all/>
and change just the name (for example) test_1
At the end, save the file
In your comment you say you "cannot use libraries like Nogokiri to parse the file". So I can only guess you're not doing full XML parsing.
Assuming you do not have to parse XML you could just use Ruby's String.replace method on the line in question. Just iterate over the lines in the file until you get to the one in quesion and call replace. Here's some pseudocode.
open file
for each line
is this what I want to change
change line
save (new) file

How to fully clean bin and obj folders within Visual Studio?

If you right click on a folder, you will see a "Clean" menu item. I assumed this would clean (remove) the obj and bin directory.
However, as far as I can see, it does nothing.
Is there another way?
(please don't tell me to go to Windows Explorer or the cmd.exe)
I'd like to remove the obj and bin folder so that I can easily zip the whole thing.
As others have responded already Clean will remove all artifacts that are generated by the build. But it will leave behind everything else.
If you have some customizations in your MSBuild project this could spell trouble and leave behind stuff you would think it should have deleted.
You can circumvent this problem with a simple change to your .*proj by adding this somewhere near the end :
<Target Name="SpicNSpan"
AfterTargets="Clean">
<RemoveDir Directories="$(OUTDIR)"/>
</Target>
Which will remove everything in your bin folder of the current platform/configuration.
------ Edit
Slight evolution based on Shaman's answer below (share the votes and give him some too)
<Target Name="SpicNSpan" AfterTargets="Clean">
<!-- Remove obj folder -->
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
<!-- Remove bin folder -->
<RemoveDir Directories="$(BaseOutputPath)" />
</Target>
---- Edit again with parts from xDisruptor but I removed the .vs deletion as this would be better served in a .gitignore (or equivalent)
Updated for VS 2015.
<Target Name="SpicNSpan" AfterTargets="Clean"> <!-- common vars https://msdn.microsoft.com/en-us/library/c02as0cs.aspx?f=255&MSPPError=-2147217396 -->
<RemoveDir Directories="$(TargetDir)" /> <!-- bin -->
<RemoveDir Directories="$(ProjectDir)$(BaseIntermediateOutputPath)" /> <!-- obj -->
</Target>
He also provides a good suggestion on making the task easier to deploy and maintain if you have multiple projects to push this into.
If you vote this answer be sure to vote them both as well.
If you are using git and have a correct .gitignore in your project, you can
git clean -xdf --dry-run
to remove absolutely every file on the .gitignore list, i.e. it will clean obj, and bin folders (the x triggers this behavior)
Note: The parameter --dry-run will only simulate the operation ("Would remove ...") and show you what git would delete. Try it with dry-run, then remove the parameter and it will really delete the files+folders.
Optionally, after that clean command, you can use dotnet restore mySolution.sln to get all the NUGET packages restored. And if you have a developer console open anyway, you can quickly run msbuild -m mySolution.sln afterwards (without having Visual Studio open) to see if it was successful.
For Visual Studio 2015 the MSBuild variables have changed a bit:
<Target Name="SpicNSpan" AfterTargets="Clean"> <!-- common vars https://msdn.microsoft.com/en-us/library/c02as0cs.aspx?f=255&MSPPError=-2147217396 -->
<RemoveDir Directories="$(TargetDir)" /> <!-- bin -->
<RemoveDir Directories="$(SolutionDir).vs" /> <!-- .vs -->
<RemoveDir Directories="$(ProjectDir)$(BaseIntermediateOutputPath)" /> <!-- obj -->
</Target>
Notice that this snippet also wipes out the .vs folder from the root directory of your solution. You may want to comment out the associated line if you feel that removing the .vs folder is an overkill. I have it enabled because I noticed that in some third party projects it causes issues when files ala application.config exist inside the .vs folder.
Addendum:
If you are into optimizing the maintainability of your solutions you might want to take things one step further and place the above snippet into a separate file like so:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="SpicNSpan" AfterTargets="Clean"> <!-- common vars https://msdn.microsoft.com/en-us/library/c02as0cs.aspx?f=255&MSPPError=-2147217396 -->
<RemoveDir Directories="$(TargetDir)" /> <!-- bin -->
<RemoveDir Directories="$(SolutionDir).vs" /> <!-- .vs -->
<RemoveDir Directories="$(ProjectDir)$(BaseIntermediateOutputPath)" /> <!-- obj -->
</Target>
</Project>
And then include this file at the very end of each and every one of your *.csproj files like so:
[...]
<Import Project="..\..\Tools\ExtraCleanup.targets"/>
</Project>
This way you can enrich or fine-tune your extra-cleanup-logic centrally, in one place without going through the pains of manually editing each and every *.csproj file by hand every time you want to make an improvement.
To delete bin and obj before build add to project file:
<Target Name="BeforeBuild">
<!-- Remove obj folder -->
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
<!-- Remove bin folder -->
<RemoveDir Directories="$(BaseOutputPath)" />
</Target>
Here is article: How to remove bin and/or obj folder before the build or deploy
This site: https://sachabarbs.wordpress.com/2014/10/24/powershell-to-clean-visual-studio-binobj-folders/ uses William Kempf's powershell commands to remove any bin and obj folders from the current directory and sub directories. It should be possible to run it from the root of the drive.
Here is William's version
gci -inc bin,obj -rec | rm -rec -force
In William's own words:
That wipes out all of the “bin” and “obj” directories in the current
directory and every subdirectory. Super useful to run in your
workspace directory to get to a “clean” state, especially when someone
messes up and there’s something that a Clean or Rebuild inside the IDE
doesn’t catch.
For those of you reading that may not know, PowerShell supports
command aliases, here it is rewritten again not using the aliases
Get-ChildItem -inc bin,obj -rec | Remove-Item -rec -force
NOTE : You should have this stored in a PowerShell file and place that
file at the root of your solution (where the .sln file resides), and
then run it when you want a proper clean (not the micky mouse one that
VisualStudio does, and reports success too).
Check out Ron Jacobs fantastic open source CleanProject It even takes care of the zipping if you like.
Here is the CodePlex link
Visual Studio Extension
Right Click Solution - Select "Delete bin and obj folders"
You can easily find and remove bin and obj folders in Far Manager.
Navigate to you solution and press Alt+F7
In search setting dialog:
Type "bin,obj" in field "A file mask or several file masks"
Check option "Search for folders"
Press Enter
After the search is done, switch view to "Panel".
Select all files (with Ctrl+A) and delete folders (press "Shift+Del")
Hope it helps someone.
Based on Joe answer, I've converted the VB code into C# :
/// <summary>
/// Based on code of VSProjCleaner tool (C) 2005 Francesco Balena, Code Archirects
/// </summary>
static class VisualStudioCleaner
{
public static void Process(string rootDir)
{
// Read all the folder names in the specified directory tree
string[] dirNames = Directory.GetDirectories(rootDir, "*.*", SearchOption.AllDirectories);
List<string> errorsList = new List<string>();
// delete any .suo and csproj.user file
foreach (string dir in dirNames) {
var files = new List<string>();
files.AddRange(Directory.GetFiles(dir, "*.suo"));
files.AddRange(Directory.GetFiles(dir, "*.user"));
foreach (string fileName in files) {
try {
Console.Write("Deleting {0} ...", fileName);
File.Delete(fileName);
Console.WriteLine("DONE");
} catch (Exception ex) {
Console.WriteLine();
Console.WriteLine(" ERROR: {0}", ex.Message);
errorsList.Add(fileName + ": " + ex.Message);
}
}
}
// Delete all the BIN and OBJ subdirectories
foreach (string dir in dirNames) {
string dirName = Path.GetFileName(dir).ToLower();
if (dirName == "bin" || dirName == "obj") {
try {
Console.Write("Deleting {0} ...", dir);
Directory.Delete(dir, true);
Console.WriteLine("DONE");
} catch (Exception ex) {
Console.WriteLine();
Console.WriteLine(" ERROR: {0}", ex.Message);
errorsList.Add(dir + ": " + ex.Message);
}
}
}
Console.WriteLine(new string('-', 60));
if (errorsList.Count == 0) {
Console.WriteLine("All directories and files were removed successfully");
} else {
Console.WriteLine("{0} directories or directories couldn't be removed", errorsList.Count);
Console.WriteLine(new string('-', 60));
foreach (string msg in errorsList) {
Console.WriteLine(msg);
}
}
}
}
In windows just open the explorer
navigate to your SLN folder
click into search field and type kind:=folder;obj --> for obj folders
use CTRL+A and delete 'em - same for bin
Done
No need for any tool or extra software ;)
Clean will remove all intermediate and final files created by the build process, such as .obj files and .exe or .dll files.
It does not, however, remove the directories where those files get built.
I don't see a compelling reason why you need the directories to be removed.
Can you explain further?
If you look inside these directories before and after a "Clean", you should see your compiled output get cleaned up.
I use VisualStudioClean which is easy to understand and predictable. Knowing how it works and what files it is going to delete relieves me.
Previously I tried VSClean (note VisualStudioClean is not VSClean), VSClean is more advanced, it has many configurations that sometimes makes me wondering what files it is going to delete? One mis-configuration will result in lose of my source codes. Testing how the configuration will work need backing up all my projects which take a lot of times, so in the end I choose VisualStudioClean instead.
Conclusion : VisualStudioClean if you want basic cleaning, VSClean for more complex scenario.
I can't add a comment yet (no minimal reputation reached)
so I leave this reply to underline that:
the "BeforeBuild" action with <RemoveDir Directories="$(BaseIntermediateOutputPath)" /> is great but, for me, is conflicting with an Entity Framework model included into the same project.
The error I receive is:
Error reading resource '{mymodel}.csdl' -- 'Could not find a part of the path '{myprojectpath}\obj\Release\edmxResourcesToEmbed\{mymodel}.csdl
I suppose, the "edmxResourcesToembed" is created before the "BeforeBuild" target action is executed.
This is how I do with a batch file to delete all BIN and OBJ folders recursively.
Create an empty file and name it DeleteBinObjFolders.bat
Copy-paste code the below code into the DeleteBinObjFolders.bat
Move the DeleteBinObjFolders.bat file in the same folder with your solution (*.sln) file.
#echo off
#echo Deleting all BIN and OBJ folders...
for /d /r . %%d in (bin,obj) do #if exist "%%d" rd /s/q "%%d"
#echo BIN and OBJ folders successfully deleted :) Close the window.
pause > nul
Update: Visual Studio 2019 (Clean [bin] and [obj] before release). However I am not sure if [obj] needs to be deleted. Be aware there is nuget package configuration placed too. You can remove the second line if you think so.
<Target Name="PreBuild" BeforeTargets="PreBuildEvent" Condition="'$(Configuration)' == 'Release'">
<!--remove bin-->
<Exec Command="rd /s /q "$(ProjectDir)$(BaseOutputPath)" && ^" />
<!--remove obj-->
<Exec Command="rd /s /q "$(BaseIntermediateOutputPath)Release"" />
</Target>
I store my finished VS projects by saving only source code.
I delete BIN, DEBUG, RELEASE, OBJ, ARM and .vs folders from all projects.
This reduces the size of the project considerably. The project
must be rebuilt when pulled out of storage.
Just an addendum to all the fine answers above in case someone doesn't realize how easy it is in VB/C# to automate the entire process down to the zip archive.
So you just grab a simple Forms app from the templates (if you don't already have a housekeeping app) and add a button to it and then ClickOnce install it to your desktop without worrying about special settings or much of anything. This is all the code you need to attach to the button:
Imports System.IO.Compression
Private Sub btnArchive_Click(sender As Object, e As EventArgs) Handles btnArchive.Click
Dim src As String = "C:\Project"
Dim dest As String = Path.Combine("D:\Archive", "Stub" & Now.ToString("yyyyMMddHHmmss") & ".zip")
If IsProjectOpen() Then 'You don't want Visual Studio holding a lock on anything while you're deleting folders
MsgBox("Close projects first, (expletive deleted)", vbOKOnly)
Exit Sub
End If
If MsgBox("Are you sure you want to delete bin and obj folders?", vbOKCancel) = DialogResult.Cancel Then Exit Sub
If ClearBinAndObj(src) Then ZipFile.CreateFromDirectory(src, dest)
End Sub
Public Function ClearBinAndObj(targetDir As String) As Boolean
Dim dirstodelete As New List(Of String)
For Each d As String In My.Computer.FileSystem.GetDirectories(targetDir, FileIO.SearchOption.SearchAllSubDirectories, "bin")
dirstodelete.Add(d)
Next
For Each d As String In My.Computer.FileSystem.GetDirectories(targetDir, FileIO.SearchOption.SearchAllSubDirectories, "obj")
dirstodelete.Add(d)
Next
For Each d In dirstodelete
Try
Directory.Delete(d, True)
Catch ex As Exception
If MsgBox("Error: " & ex.Message & " - OK to continue?", vbOKCancel) = MsgBoxResult.Cancel Then Return False
End Try
Next
Return True
End Function
Public Function IsProjectOpen()
For Each clsProcess As Process In Process.GetProcesses()
If clsProcess.ProcessName.Equals("devenv") Then Return True
Next
Return False
End Function
One thing to remember is that file system deletes can go wrong easily. One of my favorites was when I realized that I couldn't delete a folder because it contained items created by Visual Studio while running with elevated privileges (so that I could debug a service).
I needed to manually give permission or, I suppose, run the app with elevated privileges also. Either way, I think there is some value in using an interactive GUI-based approach over a script, specially since this is likely something that is done at the end of a long day and you don't want to find out later that your backup doesn't actually exist...
this answer is great I just want to comment on the last part of the answer
NOTE : You should have this stored in a PowerShell file and place that
file at the root of your solution (where the .sln file resides), and
then run it when you want a proper clean (not the micky mouse one that
VisualStudio does, and reports success too).
Alternatively, you can add the following to your profile.ps1
function CleanSolution {
Get-ChildItem -inc bin,obj -rec | Remove-Item -rec -force
}
Set-Alias cs CleanSolution
Then you can use either CleanSolution or cs to run. That way you can use it for any project and without the ./ prefix of the filename
Complete one-liner you can invoke from within Visual Studio
In your solution root folder create a file called "CleanBin.bat" and add the following one-liner:
Powershell.exe -ExecutionPolicy Bypass -NoExit -Command "Get-ChildItem -inc bin,obj -rec | Remove-Item -rec -force"
Run the .bat file. Enjoy.
Original creds to the answer here: https://stackoverflow.com/a/43267730/1402498
The original answer shows the powershell command, but I had a lot of trouble making it work smoothly on my system. I finally arrived at the one-liner above, which should work nicely for most folks.
Caveat:
Microsoft seems to be great at making Windows security cause stranger and stranger behavior. On my machine, when I run the script, all obj and bin folders are deleted but then reappear 2 seconds later! Running the script a second time causes permanent deletion. If anyone knows what would cause this behavior, please let me know a fix and I'll update the answer.
for visual studio 2022
you can use:
https://marketplace.visualstudio.com/items?itemName=MadsKristensen.CleanSolution
If you need to delete bin and obj folders from ALL of your projects...
Launch git Bash and enter the following command:
find . -iname "bin" -o -iname "obj" | xargs rm -rf
For C# projects, I recommend appending $(Configuration) to obj folder, so-as to avoid deleting nuget files which are stored on obj base directory.
<Target Name="CleanAndDelete" AfterTargets="Clean">
<!-- Remove obj folder -->
<RemoveDir Directories="$(BaseIntermediateOutputPath)$(Configuration)" />
<!-- Remove bin folder -->
<RemoveDir Directories="$(BaseOutputPath)" />
</Target>
If you delete the nuget files, it can be problematic to recreate them. Moreover, I've never seen a case where "Restore NuGet Packages" fixes this issue after these files have been deleted.

Resources