I have a solution with Web Site project. There are three custom Solution configuration: Dev, Test, Prod. However, Web Site project doesn't have .csproj file and there is no way(or I can't find it out) to use configurations for Web Site project.
I'm using MSBuild to build the solution:
MSBuild.exe MySolution.sln /m /p:Platform=x86;TargetFrameworkVersion=v4.6.2;OutputPath=bin;Configuration=Dev /ToolsVersion:14.0
As .sln file doesn't have any information about my Dev configuration for Web Site project, there are only Debug and Release "sections":
Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Web", "Web\", "{1F81A0DB-6FF4-4F89-B988-6D327797C4FD}"
ProjectSection(WebsiteProperties) = preProject
TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.6.2"
ProjectReferences = "{12A625AA-8B5A-4336-AA4A-3630AAB3A27D}|MyLib.dll;"
Debug.AspNetCompiler.VirtualPath = "/localhost_54141"
Debug.AspNetCompiler.PhysicalPath = "Web\"
Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_54141\"
Debug.AspNetCompiler.Updateable = "true"
Debug.AspNetCompiler.ForceOverwrite = "true"
Debug.AspNetCompiler.FixedNames = "false"
Debug.AspNetCompiler.Debug = "True"
Release.AspNetCompiler.VirtualPath = "/localhost_54141"
Release.AspNetCompiler.PhysicalPath = "Web\"
Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_54141\"
Release.AspNetCompiler.Updateable = "true"
Release.AspNetCompiler.ForceOverwrite = "true"
Release.AspNetCompiler.FixedNames = "false"
Release.AspNetCompiler.Debug = "False"
VWDPort = "54141"
SlnRelativePath = "Web\"
StartServerOnDebug = "false"
EndProjectSection
EndProject
And Web Site is not being built.
OK, I can manually edit solution file and change/add Dev.XXXX, Test.XXX etc related settings and everything is working well until you open solution in Visual Studio and save it. Visual Studio discharges all your changes and you are faced with the same problem again.
As a workaround, I can create a copy of .sln file and manually synchronize it with the original solution. Does anybody have a better idea?
You can build the Web Site project by using different custom AspNetCompiler MSBuild task. like this:
dev.msbuild
<Project xmlns = "http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name = "PrecompileWeb">
<AspNetCompiler
VirtualPath = "/dev"
PhysicalPath = "D:\Project\WebSite1\"
TargetPath = "PrecompiledWeb\dev\"
Force = "true"
Debug = "False"
Updateable = "true"/>
</Target>
</Project>
Test.msbuild
<Project xmlns = "http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name = "PrecompileWeb">
<AspNetCompiler
VirtualPath = "/test"
PhysicalPath = "D:\Project\WebSite1\"
TargetPath = "PrecompiledWeb\test\"
Force = "true"
Debug = "False"
Updateable = "true"/>
</Target>
</Project>
build command like this:
MSBuild.exe dev.msbuild /m /p:TargetFrameworkVersion=v4.6.2;OutputPath=bin /ToolsVersion:14.0
Related
I have a visual studio setup project that I am using for on one of my project. I shows me lot of dependencies that I don't want included in my MSI, so I excluded them (from VS UI). But for some reason when I use the command line msbuild it VS2010 still looks for the excluded dependencies. Then I decided to open the vdproj file in the text editor and then manually delete all the dependencies. So now my local msbuild problem is solved but when I build using the TFS build it reinjects all the dependencies into the MSI.
Ex:
"{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_EE52F4998F1347C63BF8AE8E07278827"
{
"AssemblyRegister" = "3:1"
"AssemblyIsInGAC" = "11:FALSE"
"AssemblyAsmDisplayName" = "8:myproject.Test, Version=3.3.0.0, Culture=neutral, processorArchitecture=MSIL"
"ScatterAssemblies"
{
"_EE52F4998F1347C63BF8AE8E07278827"
{
"Name" = "8:Test.testmsi.DLL"
"Attributes" = "3:512"
}
}
"SourcePath" = "8:test.testmsi.SR.DLL"
"TargetName" = "8:"
"Tag" = "8:"
"Folder" = "8:_289AAB175D4E4DA9B94AC6756E51F3F9"
"Condition" = "8:"
"Transitive" = "11:FALSE"
"Vital" = "11:TRUE"
"ReadOnly" = "11:FALSE"
"Hidden" = "11:FALSE"
"System" = "11:FALSE"
"Permanent" = "11:FALSE"
"SharedLegacy" = "11:FALSE"
"PackageAs" = "3:1"
"Register" = "3:1"
"Exclude" = "11:TRUE"
"IsDependency" = "11:TRUE"
"IsolateTo" = "8:"
}
So basically what I did was delete all these section of the file which has
"Exclude" = "11:TRUE"
"IsDependency" = "11:TRUE"
Is there any thing else I need to remove from the file so that TFS does not inject other stuff in the MSI?
Thanks
Automatic dependency detection is your enemy.
You can minimize the impact by only working on the installer project on the build machine, checking in via the build machine, and only in the final release configuration. Then the exclusions 'hold'. At least they do for me.
See my whinge about this component here.
This helped me resolve my problem:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/cccfa305-e8c2-4cd2-8503-78e9fc2028cb/setup-build-fails-unable-to-update-the-dependencies-of-the-project?forum=netfxbcl
dismantling the setup project (remove all assemblies), then save the setup project, then edit the setup project file and remove assemblies which remain. Then re-add everything back from within Visual Studio. Then, exclude Detected Dependencies. Had to manually update IDs for shortcuts to point to the executable. The setup kits can be built once again.
My specific problem is how can I automate "add-migration" in a build process for the Entity Framework. In researching this, it seems the mostly likely approach is something along the lines of automating these steps
Open a solution in Visual Studio 2013
Execute "Add-Migration blahblah" in the Package Manager Console (most likely via an add-in vsextention)
Close the solution
This initial approach is based on my own research and this question, the powershell script ultimately behind Add-Migration requires quite a bit of set-up to run. Visual Studio performs that setup automatically when creating the Package Manager Console and making the DTE object available. I would prefer not to attempt to duplicate that setup outside of Visual Studio.
One possible path to a solution is this unanswered stack overflow question
In researching the NuGet API, it does not appear to have a "send this text and it will be run like it was typed in the console". I am not clear on the lines between Visual Studio vs NuGet so I am not sure this is something that would be there.
I am able to find the "Pacakage Manager Console" ironically enough via "$dte.Windows" command in the Package Manager Console but in a VS 2013 window, that collection gives me objects which are "Microsoft.VisualStudio.Platform.WindowManagement.DTE.WindowBase". If there is a way stuff text into it, I think I need to get it to be a NuGetConsole.Implementation.PowerConsoleToolWindow" through reviewing the source code I am not clear how the text would stuffed but I am not at all familiar with what I am seeing.
Worst case, I will fall back to trying to stuff keys to it along the lines of this question but would prefer not to since that will substantially complicate the automation surrounding the build process.
All of that being said,
Is it possible to stream commands via code to the Package Manager Console in Visual Studio which is fully initialized and able to support an Entity Framework "add-migration" command?
Thanks for any suggestions, advice, help, non-abuse in advance,
John
The approach that worked for me was to trace into the entity framework code starting in with the AddMigrationCommand.cs in the EntityFramework.Powershell project and find the hooks into the EntityFramework project and then make those hooks work so there is no Powershell dependency.
You can get something like...
public static void RunIt(EnvDTE.Project project, Type dbContext, Assembly migrationAssembly, string migrationDirectory,
string migrationsNamespace, string contextKey, string migrationName)
{
DbMigrationsConfiguration migrationsConfiguration = new DbMigrationsConfiguration();
migrationsConfiguration.AutomaticMigrationDataLossAllowed = false;
migrationsConfiguration.AutomaticMigrationsEnabled = false;
migrationsConfiguration.CodeGenerator = new CSharpMigrationCodeGenerator(); //same as default
migrationsConfiguration.ContextType = dbContext; //data
migrationsConfiguration.ContextKey = contextKey;
migrationsConfiguration.MigrationsAssembly = migrationAssembly;
migrationsConfiguration.MigrationsDirectory = migrationDirectory;
migrationsConfiguration.MigrationsNamespace = migrationsNamespace;
System.Data.Entity.Infrastructure.DbConnectionInfo dbi = new System.Data.Entity.Infrastructure.DbConnectionInfo("DataContext");
migrationsConfiguration.TargetDatabase = dbi;
MigrationScaffolder ms = new MigrationScaffolder(migrationsConfiguration);
ScaffoldedMigration sf = ms.Scaffold(migrationName, false);
}
You can use this question to get to the dte object and from there to find the project object to pass into the call.
This is an update to John's answer whom I have to thank for the "hard part", but here is a complete example which creates a migration and adds that migration to the supplied project (project must be built before) the same way as Add-Migration InitialBase -IgnoreChanges would:
public void ScaffoldedMigration(EnvDTE.Project project)
{
var migrationsNamespace = project.Properties.Cast<Property>()
.First(p => p.Name == "RootNamespace").Value.ToString() + ".Migrations";
var assemblyName = project.Properties.Cast<Property>()
.First(p => p.Name == "AssemblyName").Value.ToString();
var rootPath = Path.GetDirectoryName(project.FullName);
var assemblyPath = Path.Combine(rootPath, "bin", assemblyName + ".dll");
var migrationAssembly = Assembly.Load(File.ReadAllBytes(assemblyPath));
Type dbContext = null;
foreach(var type in migrationAssembly.GetTypes())
{
if(type.IsSubclassOf(typeof(DbContext)))
{
dbContext = type;
break;
}
}
var migrationsConfiguration = new DbMigrationsConfiguration()
{
AutomaticMigrationDataLossAllowed = false,
AutomaticMigrationsEnabled = false,
CodeGenerator = new CSharpMigrationCodeGenerator(),
ContextType = dbContext,
ContextKey = migrationsNamespace + ".Configuration",
MigrationsAssembly = migrationAssembly,
MigrationsDirectory = "Migrations",
MigrationsNamespace = migrationsNamespace
};
var dbi = new System.Data.Entity.Infrastructure
.DbConnectionInfo("ConnectionString", "System.Data.SqlClient");
migrationsConfiguration.TargetDatabase = dbi;
var scaffolder = new MigrationScaffolder(migrationsConfiguration);
ScaffoldedMigration migration = scaffolder.Scaffold("InitialBase", true);
var migrationFile = Path.Combine(rootPath, migration.Directory,
migration.MigrationId + ".cs");
File.WriteAllText(migrationFile, migration.UserCode);
var migrationItem = project.ProjectItems.AddFromFile(migrationFile);
var designerFile = Path.Combine(rootPath, migration.Directory,
migration.MigrationId + ".Designer.cs");
File.WriteAllText(designerFile, migration.DesignerCode);
var designerItem = project.ProjectItems.AddFromFile(migrationFile);
foreach(Property prop in designerItem.Properties)
{
if (prop.Name == "DependentUpon")
prop.Value = Path.GetFileName(migrationFile);
}
var resxFile = Path.Combine(rootPath, migration.Directory,
migration.MigrationId + ".resx");
using (ResXResourceWriter resx = new ResXResourceWriter(resxFile))
{
foreach (var kvp in migration.Resources)
resx.AddResource(kvp.Key, kvp.Value);
}
var resxItem = project.ProjectItems.AddFromFile(resxFile);
foreach (Property prop in resxItem.Properties)
{
if (prop.Name == "DependentUpon")
prop.Value = Path.GetFileName(migrationFile);
}
}
I execute this in my project template's IWizard implementation where I run a migration with IgnoreChanges, because of shared entites with the base project. Change scaffolder.Scaffold("InitialBase", true) to scaffolder.Scaffold("InitialBase", false) if you want to include the changes.
In our product, there are around 400 projects, so in VS 2012, if I want to make a build then it generates code analysis for all 400 projects and I can't manually disable code analysis for each & every project. So I am looking for a mechanism which disables code analysis for entire solution (all projects) rather then applying those settings to individual projects.
You can use a little trick to disable the static code analysis for a whole Visual Studio instance as described here. In short:
Open a Developer Command Prompt for VS2012
type set DevDivCodeAnalysisRunType=Disabled
type devenv to start Visual Studio
Same solution works for Visual Studio 2015 via Developer Command Prompt for VS2015.
Not sure there is an easy to do this with VS2012. CodeAnalysis is defined at project level and depends on your build configuration. For example, there is no code analysis in Release.
First, try to create a configuration based on Release.
Another solution (but very bad) may be to run a batch to modify all your project files.
Here is a sample project file (check the element named RunCodeAnalysis) :
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
You could write a little console application that reads all the project files out of a solution file and then toggle the Xml Node of each project.
Function to get the project files out of the solution:
public IEnumerable<string> Parse(string solutionFile)
{
if (solutionFile == null)
throw new ArgumentNullException("solutionFile");
if (!File.Exists(solutionFile))
throw new FileNotFoundException("Solution file does not exist", solutionFile);
var projectFiles = new List<string>();
using (var reader = new StreamReader(solutionFile, true))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line == null)
continue;
line = line.TrimStart();
if (!line.StartsWith("Project(", StringComparison.OrdinalIgnoreCase))
continue;
var projectData = line.Split(',');
var projectFile = projectData[1].Trim().Trim('"');
if (!string.IsNullOrEmpty(Path.GetExtension(projectFile)))
projectFiles.Add(projectFile);
}
}
return projectFiles;
}
And the function to toggle the RunCodeAnalysis Node(s):
public void ToggleCodeAnalysis(string projectFile)
{
if (projectFile == null)
throw new ArgumentNullException("projectFile");
if (!File.Exists(projectFile))
throw new FileNotFoundException("Project file does not exist", projectFile);
var xmlDocument = new XmlDocument();
xmlDocument.Load(projectFile);
var namespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);
namespaceManager.AddNamespace("ns", "http://schemas.microsoft.com/developer/msbuild/2003");
var nodes = xmlDocument.SelectNodes("//ns:RunCodeAnalysis", namespaceManager);
if (nodes == null)
return;
var hasChanged = false;
foreach (XmlNode node in nodes)
{
bool value;
if (!Boolean.TryParse(node.InnerText, out value))
continue;
node.InnerText = value ? "false" : "true";
hasChanged = true;
}
if (!hasChanged)
return;
xmlDocument.Save(projectFile);
}
You can use the Package Manager Console window of Nuget to do that.
Create a new text file in the directory "C:\Users{your user}\Documents\WindowsPowerShell" named "NuGet_profile.ps1" and add the following code:
function Disable-CodeAnalysis(){
ForEach ($project in $dte.Solution.Projects) {
Set-CodeAnalysis($project, $false)
}
}
function Enable-CodeAnalysis(){
ForEach ($project in $dte.Solution.Projects) {
Set-CodeAnalysis($project, $true)
}
}
function Set-CodeAnalysis($project, $value){
$projectName = $project.Name
$projectFilePath = $project.FullName
if ([System.String]::IsNullOrWhiteSpace($projectFilePath)){
if($proj.Kind -eq "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}"){
ForEach ($item in $proj.ProjectItems) {
if($item.SubProject -ne $null){
Set-CodeAnalysis($item.SubProject, $value)
}
}
}
continue;
}
$xmlDocument = new-object System.Xml.XmlDocument
$action = "Enable"
if($value -ne $true){
$action = "Disable"
}
Write-Host "$action code analysis for $projectName at $projectFilePath"
$xmlDocument.Load([string]$projectFilePath);
$namespaceManager = new-object System.Xml.XmlNamespaceManager($xmlDocument.NameTable);
$namespaceManager.AddNamespace("ns", "http://schemas.microsoft.com/developer/msbuild/2003");
$nodes = $xmlDocument.SelectNodes("//ns:RunCodeAnalysis", $namespaceManager);
if ($nodes -eq $null){
continue;
}
foreach ($node in $nodes){
$node.InnerText = "$value";
}
$xmlDocument.Save($projectFilePath);
}
Restart Visual Studio. Click the menu "View" | "Other Windows" | "Package Manager Console". Now you can execute the following commands:
> Enable-CodeAnalysis
> Disable-CodeAnalysis
To disable code analysis for particular project :-
Right click on Project , select Properties.
On properties page select Code Analysis.
Uncheck the check box of "Enable Code Analysis on Build".
I have a VS 2010 solution with a number of projects in it.
Projects reference other projects within the solution.
I have noticed that when I have a wrong project reference path in a csproj file like this:
<ProjectReference Include="..\..\..\..\WrongFolder\OtherProject.csproj">
<Project>{CD795AA6-9DC4-4451-A8BA-29BACF847AAC}</Project>
<Name>OtherProject</Name>
</ProjectReference>
Visual studio would fix this on opening the solution:
<ProjectReference Include="..\..\..\..\RightFolder\OtherProject.csproj">
<Project>{CD795AA6-9DC4-4451-A8BA-29BACF847AAC}</Project>
<Name>OtherProject</Name>
</ProjectReference>
I suppose it uses the GUID from the Project element to uniquely identify the project within the solution which allows it to fix the path.
MSBuild on the other hand doesn't seem to fix this path and building the solution fails.
Is there a way to make MSBuild fix the path or do it as a pre-build step with some other tool or command so that the solution builds correctly?
Thanks!
This is part of the VisualStudio functionality. But you can call a tool to solve the references before the build. Here is a draft code that you can elaborate:
using System;
using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml;
namespace FixProjectReferences
{
class Program
{
// License: This work is licensed under a Creative Commons
// Attribution-ShareAlike 3.0 Unported License.
// Author: Marlos Fabris
// Summary: Updates the project references in csproj.
// Param:
// args[0] = Main project (c:\mainProject.csproj)
// args[1] = Folder to scan other projects (c:\other)
static void Main(string[] args)
{
string mainProject = args[0];
string folder = args[1];
FileInfo mainPrjInfo = new FileInfo(mainProject);
string currentDir = Directory.GetCurrentDirectory();
// Lists all project files in the directory specified
// and scans the GUID's.
DirectoryInfo info = new DirectoryInfo(folder);
FileInfo[] projects = info.GetFiles("*.csproj",
SearchOption.AllDirectories);
Dictionary<Guid, string> prjGuids = new Dictionary<Guid, string>();
foreach (var project in projects)
{
if (project.FullName == mainPrjInfo.FullName)
continue;
Regex regex = new Regex("<ProjectGuid>(\\{.*?\\})</ProjectGuid>");
Match match = regex.Match(File.ReadAllText(project.FullName));
string guid = match.Groups[1].Value;
prjGuids.Add(new Guid(guid), project.FullName);
}
// Loads the main project and verifies if the references are valid.
// If not, updates with the correct ones from the list
// previously generated.
XmlDocument doc = new XmlDocument();
doc.Load(mainPrjInfo.FullName);
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("ns",
"http://schemas.microsoft.com/developer/msbuild/2003");
var nodes = doc.SelectNodes("//ns:ProjectReference", ns);
foreach (XmlNode node in nodes)
{
string referencePath = node.Attributes["Include"].Value;
string path = Path.Combine(mainPrjInfo.Directory.FullName,
referencePath);
if (File.Exists(path))
continue;
string projectGuid = node.SelectSingleNode("./ns:Project",
ns).InnerText;
Guid tempGuid = new Guid(projectGuid);
if (prjGuids.ContainsKey(tempGuid))
{
node.Attributes["Include"].Value = prjGuids[tempGuid];
}
}
doc.Save(mainPrjInfo.FullName);
}
}
}
I've checked out a branch of C# code from source control. It contains maybe 50 projects in various folders. There's no existing .sln file to be found.
I intended to create a blank solution to add existing solutions. The UI only lets me do this one project at a time.
Is there something I'm missing? I'd like to specify a list of *.csproj files and somehow come up with a .sln file that contains all the projects.
A PowerShell implementation that recursively scans the script directory for .csproj files and adds them to a (generated) All.sln:
$scriptDirectory = (Get-Item $MyInvocation.MyCommand.Path).Directory.FullName
$dteObj = [System.Activator]::CreateInstance([System.Type]::GetTypeFromProgId("VisualStudio.DTE.12.0"))
$slnDir = ".\"
$slnName = "All"
$dteObj.Solution.Create($scriptDirectory, $slnName)
(ls . -Recurse *.csproj) | % { $dteObj.Solution.AddFromFile($_.FullName, $false) }
$dteObj.Solution.SaveAs( (Join-Path $scriptDirectory 'All.sln') )
$dteObj.Quit()
A C# implementation that produces an executable, which creates a solution containing all unique *.csproj files from the directory and subdirectories it is executed in.
class Program
{
static void Main(string[] args)
{
using (var writer = new StreamWriter("All.sln", false, Encoding.UTF8))
{
writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 11.00");
writer.WriteLine("# Visual Studio 2010");
var seenElements = new HashSet<string>();
foreach (var file in (new DirectoryInfo(System.IO.Directory.GetCurrentDirectory())).GetFiles("*.csproj", SearchOption.AllDirectories))
{
string fileName = Path.GetFileNameWithoutExtension(file.Name);
if (seenElements.Add(fileName))
{
var guid = ReadGuid(file.FullName);
writer.WriteLine(string.Format(#"Project(""0"") = ""{0}"", ""{1}"",""{2}""", fileName, file.FullName, guid));
writer.WriteLine("EndProject");
}
}
}
}
static Guid ReadGuid(string fileName)
{
using (var file = File.OpenRead(fileName))
{
var elements = XElement.Load(XmlReader.Create(file));
return Guid.Parse(elements.Descendants().First(element => element.Name.LocalName == "ProjectGuid").Value);
}
}
}
There is extension for VS available, capable of adding all projects in selected directory (and more):
http://www.cyotek.com/blog/visual-studio-extension-for-adding-multiple-projects-to-a-solution
Use Visual Studio Extension "Add Existing Projects". It works with Visual Studio 2012, 2013, 2015, 2017.
To use the extension, open the Tools menu and choose Add Projects.
Plenty of answers here already, but none quite as clear as this powershell oneliner
Get-ChildItem -Recurse -Include *.csproj | ForEach-Object { dotnet sln add $_ }
You might be able to write a little PowerShell script or .NET app that parses all the projects' .csproj XML and extracts their details (ProjectGuid etc.) then adds them into the .sln file. It'd be quicker and less risky to add them all by hand, but an interesting challenge nonetheless.
Note: This is only for Visual Studio 2010
Found here is a cool add in for Visual Studio 2010 that gives you a PowerShell console in VS to let you interact with the IDE. Among many other things you can do using the built in VS extensibility as mentioned by #Avram, it would be pretty easy to add files or projects to a solution.
Here is a PowerShell version of Bertrand's script which assumes a Src and Test directory next to the solution file.
function GetGuidFromProject([string]$fileName) {
$content = Get-Content $fileName
$xml = [xml]$content
$obj = $xml.Project.PropertyGroup.ProjectGuid
return [Guid]$obj[0]
}
$slnPath = "C:\Project\Foo.sln"
$solutionDirectory = [System.IO.Path]::GetDirectoryName($slnPath)
$srcPath = [System.IO.Path]::GetDirectoryName($slnPath)
$writer = new-object System.IO.StreamWriter ($slnPath, $false, [System.Text.Encoding]::UTF8)
$writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 12.00")
$writer.WriteLine("# Visual Studio 2013")
$projects = gci $srcPath -Filter *.csproj -Recurse
foreach ($project in $projects) {
$fileName = [System.IO.Path]::GetFileNameWithoutExtension($project)
$guid = GetGuidFromProject $project.FullName
$slnRelativePath = $project.FullName.Replace($solutionDirectory, "").TrimStart("\")
# Assume the project is a C# project {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
$writer.WriteLine("Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""$fileName"", ""$slnRelativePath"",""{$($guid.ToString().ToUpper())}""")
$writer.WriteLine("EndProject")
}
$writer.Flush()
$writer.Close()
$writer.Dispose()
if you open the sln file with notepad you can see the format of the file which is easy to understand but for more info take a look # Hack the Project and Solution Files .understanding the structure of the solution files you can write an application which will open all project files and write the application name ,address and GUID to the sln file .
of course I think if it's just once you better do it manually
Every answer seems to flatten the directory structure (all the projects are added to the solution root, without respecting the folder hierarchy). So, I coded my own console app that generates the solution and uses solution folders to group them.
Check out the project in GitHub
Usage
SolutionGenerator.exe --folder C:\git\SomeSolutionRoot --output MySolutionFile.sln
when you have dotnet core installed, you can execute this from git bash:
donet new sln; find . -name "*.csproj" -exec dotnet sln add {} \;
the generated solution works with csproj created for old .NET Framework.
Depends on visual studio version.
But the name of this process is "Automation and Extensibility for Visual Studio"
http://msdn.microsoft.com/en-us/library/t51cz75w.aspx
Check this out:
http://nprove.codeplex.com/
It is a free addin for vs2010 that does that and more
if the projects are under the tfs
Building on Bertrand's answer at https://stackoverflow.com/a/16069782/492 - make a console app out of this and run it in the root folder where you want the VS 2015 Solution to appear. It works for C# & VB (hey! be nice).
It overwrites anything existing but you source control, right?
Check a recently used .SLN file to see what the first few writer.WriteLine() header lines should actually be by the time you read this.
Don't worry about the project type GUID Ptoject("0") - Visual Studio will work that out and write it in when you save the .sln file.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace AddAllProjectsToNewSolution
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("starting");
using (var writer = new StreamWriter("AllProjects.sln", false, Encoding.UTF8))
{
writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 14.00");
writer.WriteLine("# Visual Studio 14");
writer.WriteLine("VisualStudioVersion = 14.0.25420.1");
var seenElements = new HashSet<string>();
foreach (var file in (new DirectoryInfo(Directory.GetCurrentDirectory())).GetFiles("*.*proj", SearchOption.AllDirectories))
{
string extension = file.Extension;
if (extension != ".csproj" && extension != ".vbproj")
{
Console.WriteLine($"ignored {file.Name}");
continue;
}
Console.WriteLine($"adding {file.Name}");
string fileName = Path.GetFileNameWithoutExtension(file.Name);
if (seenElements.Add(fileName))
{
var guid = ReadGuid(file.FullName);
writer.WriteLine($"Project(\"0\") = \"{fileName}\", \"{GetRelativePath(file.FullName)} \", \"{{{guid}}}\"" );
writer.WriteLine("EndProject");
}
}
}
Console.WriteLine("Created AllProjects.sln. Any key to close");
Console.ReadLine();
}
static Guid ReadGuid(string fileName)
{
using (var file = File.OpenRead(fileName))
{
var elements = XElement.Load(XmlReader.Create(file));
return Guid.Parse(elements.Descendants().First(element => element.Name.LocalName == "ProjectGuid").Value);
}
}
// https://stackoverflow.com/a/703292/492
static string GetRelativePath(string filespec, string folder = null)
{
if (folder == null)
folder = Environment.CurrentDirectory;
Uri pathUri = new Uri(filespec);
// Folders must end in a slash
if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString()))
folder += Path.DirectorySeparatorChar;
Uri folderUri = new Uri(folder);
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
}
}
}
Here is Bertrand's solution updated for
Microsoft Visual Studio Solution File, Format Version 12.00
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace AllSolutionGenerator
{
internal static class Program
{
private static void Main()
{
var dir = new DirectoryInfo(Directory.GetCurrentDirectory());
var projects = new Dictionary<string, string>();
var guid1 = Guid.NewGuid().ToString().ToUpperInvariant();
using (var writer = new StreamWriter("all.sln", false, Encoding.UTF8))
{
writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 12.00");
foreach (var file in dir.GetFiles("*.csproj", SearchOption.AllDirectories))
{
var fileName = Path.GetFileNameWithoutExtension(file.Name);
if (!projects.ContainsKey(fileName))
{
var guid = Guid.NewGuid().ToString().ToUpperInvariant();
projects.Add(fileName, guid);
writer.WriteLine(#$"Project(""{{{guid1}}}"") = ""{fileName}"", ""{file.FullName}"",""{guid}""");
writer.WriteLine("EndProject");
}
}
writer.WriteLine("Global");
writer.WriteLine(" GlobalSection(SolutionConfigurationPlatforms) = preSolution");
writer.WriteLine(" Debug|Any CPU = Debug|Any CPU");
writer.WriteLine(" Release|Any CPU = Release|Any CPU");
writer.WriteLine(" EndGlobalSection");
writer.WriteLine(" GlobalSection(ProjectConfigurationPlatforms) = postSolution");
foreach (var (_, guid) in projects)
{
writer.WriteLine(#$" {{{guid}}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU");
writer.WriteLine(#$" {{{guid}}}.Debug|Any CPU.Build.0 = Debug|Any CPU");
}
writer.WriteLine(" EndGlobalSection");
writer.WriteLine("EndGlobal");
}
}
}
}
If you select 'Show all Files' in the Solution Explorer, you can than view all the files and folers and select them and right click to add them using 'Include in Project'.