6

Is there a way or an extension that does the following:

Having a *.sln file, can I list all files from all projects or all solution folders under that sln?

So if I do dotnet sln list then I get the list of the projects from that solution file, but what I need is to get all files.

My real need is to copy all those files into a different folder.

Adrian Iftode
  • 15,465
  • 4
  • 48
  • 73
  • Using assemblies like Microsoft.Build.Evaluation you can load a project file and get source files out of it (assuming thats what you want, you just specify 'files'?). Or, if you don't use wildcards to select files, parsing the project file is text is fairly easy. – stijn Oct 28 '18 at 07:29
  • Some are specified with wildcards. Some are excluded. I need all files, source code, resources, anything is included in csproj. I'll explore that api. – Adrian Iftode Oct 28 '18 at 17:19
  • Any luck? Actually, thinking of it: probably you don't even need to use the api directly, but you can use msbuild.exe from the commandline and instruct it to dump all files to a text file or so by writing a small target which does that and then have that target imported into your project files with an extension point like ImportAfter. Don't have time to write up an answer atm but I'm fairly sure that would do the trick. – stijn Oct 29 '18 at 20:50
  • Didn't check it yet. Also it's. Net core, so the format is a bit different. Probably `dotnet build` implementation has the answer. – Adrian Iftode Oct 29 '18 at 20:53
  • 2
    See here for programmatic solution: https://stackoverflow.com/questions/53070977/enumerate-files-in-a-csproj-dotnet-core – stijn Oct 31 '18 at 14:16

2 Answers2

2

Here's an msbuild-based solution which is actually fairly simple and also non-intrusive. Create a file CopySource.targets in the solution directory for instance, contents:

<Project>
  <Target Name="CopySourcesUsed" AfterTargets="AfterCompile">
    <PropertyGroup>
      <DestinationDirectory>c:\temp\dest\</DestinationDirectory>
      <SourceDirectory>$([System.IO.Path]::GetDirectoryName($(MSBuildProjectDirectory)))</SourceDirectory>
    </PropertyGroup>
    <ItemGroup>
      <CompileSources Condition="!$([System.Text.RegularExpressions.Regex]::IsMatch('%(Identity)', `.*Assembly(Attributes|Info)\.cs`))" Include="@(Compile)"/>
      <AllSources Include="@(CompileSources);@(EmbeddedResource);$(MSBuildProjectFile)" />
      <AllSources>
        <Dest>$([System.String]::new('%(FullPath)').Replace($(SourceDirectory), $(DestinationDirectory)))</Dest>
      </AllSources>
    </ItemGroup>
    <Message Importance="High" Text="%(AllSources.FullPath) -> %(AllSources.Dest)"/>
    <Copy SourceFiles="@(AllSources)" DestinationFiles="@(AllSources->'%(Dest)')" />
  </Target>
</Project>

Then run a build like

dotnet msbuild mysolution.sln /p:CustomAfterMicrosoftCSharpTargets=/path/to/CopySource.targets

(note dotnet build also works for me but might not in older versions, don't know)

This will import ListSources.Targets in each project file and run the CopySourcesUsed target after compilation. Before compilation somewhere will also work, but then you have to figure out exactly when: after compilation all items definitely have been gathered. As an example the AllSources item is filled with the compilation source files and the embedded resources. As you can see the Compile item is filtered to exclude the generated AssemblyAttributes/AssemblyInfo files since you probably don't want those. An alternative might be to have this target ran before those files are added, but again: that's just trickier. There can be other things you want to copy (xaml/config/...) in which case you have to figure out the name of the item to include. First check if it's not simply listed in the csproj. Else running the build with diagnostic verbosity can tell you the name: dotnet msbuild mysolution.sln /v:diag.

Instead of injecting this logic on the commandline you could instead import the file in each project you have, or use another extension point like making it system-wide by putting it in some ImportAfter directory, all these solutions can be found here on SO probably.

stijn
  • 34,664
  • 13
  • 111
  • 163
  • I realize that I didn't properly phrase the question. Yes this does copy all the files to the destination folder, but it doesn't preserve the same projects structure, it doesn't copy the csproj files and it doesn't copy the sln file itself also. The result of the copy operation should be `build-able` (ie dotnet build dest/target.sln should work as dotnet build src/target.sln) – Adrian Iftode Oct 31 '18 at 18:26
  • Preserving structure is a matter of adding some path manipulation (probably just replace src with dst) and copying csproj/sln isn't hard to add. But given your actual requirement: isn't it simpler to list the whole src directory and specify exclusions like bin/obj directories, then copy that? Or e.g. if you have your project in source control, use that to get the files to copy from? Also you should probably rephrase your question to state the actual question, not a possible way to do it, and perhaps provide some info about your directory structure. – stijn Oct 31 '18 at 19:14
  • While this is not a complete solution, I will mark it as answered. I will post an answer myself if I get the time to write a complete solution. – Adrian Iftode Nov 05 '18 at 06:17
  • If you want I can modify the code so it preserves directory structure etc? – stijn Nov 05 '18 at 09:11
  • Yes, please. Not only for me, but for the others that can benefit from this solution. – Adrian Iftode Nov 05 '18 at 11:01
  • 1
    Ok, see edit. Copying the .sln isn't in there because it doesn't make sense to copy this for each invocation (i.e. each project) plus if you'd be building a single project there even isn't a solution file. I'd just add that in a batch file or so which also runs the build. – stijn Nov 05 '18 at 12:25
1

Not sure if this might help, but I belive Microsoft.Build.Evaluation might help you here. There is another post about what I think is your problem:

csproj how to get all resources

I dont know with what kind of projects you are working with, I would assume csproj. But maybe this helps even if thats not the case. You should be able to modify the given example so that you get all the files you need.

Side note: It would probably helop to have a little more information about what kind of projects we are talking about, how many and if there are excluded files etc.

Benjamin Basmaci
  • 2,247
  • 2
  • 25
  • 46
  • csproj. In a folder I could have two solutions, each with its own list of csprojs. When Visual Studio builds a sln, then it knows which csprojs to look into, which files to compile, or which files to put in the output folders (ie copy to output) or which files to embed dlls. What I want to achieve is to make a clean copy of the files included that sln (via csprojs and/or solution folders). – Adrian Iftode Oct 30 '18 at 20:34
  • Currently we have a powershell script that parses the sln, creates a list of csprojs, then copies the corresponding folders. But is not very maintainable. – Adrian Iftode Oct 30 '18 at 20:35