Skip to content

Creating from scratch

Denis Kuzmin edited this page May 1, 2024 · 2 revisions

Solution files & Projects

The basis of the MvsSln is active collections at runtime. So you just need to provide some data; it can be either other processed .sln in an attempt to reuse something, or from scratch, etc.

Let's define some initial,

// your base directory for this example
string baseDir = @"C:\projects\MvsSln\";

// Define a solution configuration as `DBG|x64`
ConfigSln[] slnConf = [ new("DBG", "x64"), ];

// C# project `Example` that will be saved as C:\projects\MvsSln\Example\src.csproj
ProjectItem[] projects =
[
    new ProjectItem(ProjectType.CsSdk, @"Example\src.csproj", slnDir: baseDir),
];

// Define a project config as `Debug|x64` to a solution config `DBG|x64`
IConfPlatformPrj[] prjConfs =
[
    new ConfigPrj("Debug", "x64", projects[0].pGuid, build: true, slnConf[0]),
];

Read more about solutions and projects configurations πŸ“‘ here

Now prepare either collections of the pair of r/w handlers to process your data or ISlnWhData data for the set of default handlers.

Option 1. Map Handlers

The left part below is the read handler responsible for the scope of the processed data. Usually, when you are processing an existing .sln files, they are already located in the result map (depending on the settings) and you do not need to do anything at all. But we don't have anything yet, so ...

Dictionary<Type, HandlerValue> whandlers = new()
{
    // To handle .sln header using MakeDefault() data
    [typeof(LVisualStudioVersion)] = new(new WVisualStudioVersion(SlnHeader.MakeDefault())),

    // To handle Project lines using `projects` array above
    [typeof(LProject)] = new(new WProject(projects)),
    
    // To handle project configurations using `prjConfs` array above
    [typeof(LProjectConfigurationPlatforms)] = new(new WProjectConfigurationPlatforms(prjConfs)),
    
    // To handle prosolution configurations using `prjConfs` array above
    [typeof(LSolutionConfigurationPlatforms)] = new(new WSolutionConfigurationPlatforms(slnConf)),
};
// Initialize SlnWriter using mapped handlers above
using SlnWriter w = new(baseDir + "My.sln", whandlers);

Option 2. Wrappers

2.7+ provides some new wrappers for default handlers. In addition to option 1, you can set your data like:

LhDataHelper hdata = new();
hdata.SetHeader(SlnHeader.MakeDefault())
        .SetProjects(projects)
        .SetProjectConfigs(prjConfs)
        .SetSolutionConfigs(slnConf);
// Initialize SlnWriter using data from LhDataHelper above
using SlnWriter w = new(baseDir + "My.sln", hdata);

Actually LhDataHelper simply implements ISlnWhData. So you can do it yourself:

class MyDataHelper: ISlnWhData { ... }

Create project files if they do not exist

Just activate CreateProjectsIfNotExist before saving the result for the initialized SlnWriter

SlnWriter w ...
w.Options |= SlnWriterOptions.CreateProjectsIfNotExist;

Now all active projects will be created automatically based on their types (c++, C#, Vb, F#,) and formats (legacy or sdk-style). Note that the implementation is controlled by the IProjectsToucher interface. You can implement your own if needed:

SlnWriter w ...
w.SetToucher(myToucher);

Generate and save the result

Using default skeleton without map [?]

await w.WriteAsync();
// w.Write();

Optionally you can save the result without files as string:

// using SlnWriter w = new(hdata / whandlers) // ^remove first arg - filename
string result = await w.WriteAsStringAsync();
//string result = w.WriteAsString();

Read more about Map and default skeleton πŸ“‘ here

Update projects

Load your generated solution file with option EnvWithMinimalProjects (more about project instances πŸ“‘ here) to easily load all your generated projects:

using Sln sln = new(baseDir + "My.sln", SlnItems.EnvWithMinimalProjects);

// get the first (and only) generated project
IXProject xp = sln.Result.Env.Projects.First();

// set some properties you want
xp.SetProperties(new Dictionary<string, string>()
{
    { "OutputType", "Exe" },
    { "TargetFrameworks", "net472;net6" },
    { "LangVersion", "latest" },
});

// save the result
xp.Save();

Final code & result

string baseDir = @"C:\projects\MvsSln\";
string file = baseDir + "My.sln";

ConfigSln[] slnConf = [new("DBG", "x64")];
ProjectItem[] projects = [new ProjectItem(ProjectType.CsSdk, @"Example\src.csproj", slnDir: baseDir)];
IConfPlatformPrj[] prjConfs = [new ConfigPrj("Debug", "x64", projects[0].pGuid, build: true, slnConf[0])];

LhDataHelper hdata = new();
hdata.SetHeader(SlnHeader.MakeDefault())
        .SetProjects(projects)
        .SetProjectConfigs(prjConfs)
        .SetSolutionConfigs(slnConf);

using SlnWriter w = new(file, hdata);
w.Options = SlnWriterOptions.CreateProjectsIfNotExist;
await w.WriteAsync();

/* optionally update project files */

using Sln sln = new(file, SlnItems.EnvWithMinimalProjects);
IXProject xp = sln.Result.Env.Projects.First();

xp.SetProperties(new Dictionary<string, string>()
{
    { "OutputType", "Exe" },
    { "TargetFrameworks", "net6;net472" },
    { "LangVersion", "latest" },
});
xp.Save();

Read πŸ“‘ Custom MSBuild if you notice a problem for SlnItems.EnvWithMinimalProjects

Generated My.sln

~

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 17
VisualStudioVersion = 17.8.34525.116
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "Example\src.csproj", "{87B99093-788F-4223-B785-CB8961E196CA}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		DBG|x64 = DBG|x64
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{87B99093-788F-4223-B785-CB8961E196CA}.DBG|x64.ActiveCfg = Debug|x64
		{87B99093-788F-4223-B785-CB8961E196CA}.DBG|x64.Build.0 = Debug|x64
	EndGlobalSection
EndGlobal

Generated Example\src.csproj

C# Sdk-style format

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net6;net472</TargetFramework>
    <LangVersion>latest</LangVersion>
  </PropertyGroup>
</Project>