Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

creating projects/solutions from scratch #61

Closed
mitchcapper opened this issue Apr 12, 2024 · 6 comments
Closed

creating projects/solutions from scratch #61

mitchcapper opened this issue Apr 12, 2024 · 6 comments

Comments

@mitchcapper
Copy link

Wanted to try and create a basic project/solution but ran into a good bit of difficulty.

Below is the code I tried, I had to fallback to MS to generate the project itself and then still had problems.

While there is no example of creating a project like this there is an example of creating an issue from scratch here. Sadly the solution had several problems even (like missing global section).

Here is what 'worked' but required various manual fixes (including commented out versions of what didn't work). Not sure if there is a better way:

	Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
}
public void DoExport(String baseDir, String projName, Message msg) {

	if (Directory.Exists(baseDir))
		Directory.Delete(baseDir, true);
	Directory.CreateDirectory(baseDir);
	var solutionFile = Path.Combine(baseDir, projName + ".sln");
	var header = new SlnHeader();
	header.SetFormatVersion("12.0");
	header.SetMinimumVersion("10.0.40219.1");
	var platformData = new List<IConfPlatform>() {
		new ConfigSln("Debug", "x64"),
	};


	var projects = new[] { new ProjectItem(projName, ProjectType.CsSdk, $"{projName}.csproj") };
	var raw_proj = new WProject(projects, new LProjectDependencies());
	var platformProjData = new List<IConfPlatformPrj>() {
		new ConfigPrj(projects[0].name,projects[0].pGuid,true,platformData[0] as ConfigSln),
	};
	var whandlers = new Dictionary<Type, HandlerValue>() {
		[typeof(LVisualStudioVersion)] = new HandlerValue(new WVisualStudioVersion(header)),
		[typeof(LProject)] = new HandlerValue(raw_proj),
		//[typeof(global)] = new HandlerValue(raw_proj),
		[typeof(LProjectConfigurationPlatforms)] = new HandlerValue(new WProjectConfigurationPlatforms(platformProjData)),
		[typeof(LSolutionConfigurationPlatforms)] = new HandlerValue(new WSolutionConfigurationPlatforms(platformData)),

	};
	//  xp.AddReference(typeof(JsonConverter).Assembly, true);
	using (var w = new SlnWriter(solutionFile, whandlers)) {
		w.Write([
			new Section(new LVisualStudioVersion(), null),
			new Section(new LProject(), null),
			new Section(new LProjectConfigurationPlatforms(), null),
			new Section(new LSolutionConfigurationPlatforms(), null),
		]);

	}
	var manFixes = File.ReadAllText(solutionFile);
	manFixes = manFixes.Replace("\r", "");
	manFixes = manFixes.Replace("EndProject\n", "EndProject\nGlobal\n");
	manFixes += "\nEndGlobal\n";
	manFixes = manFixes.Replace("TestProj|", "Debug|x64");
	File.WriteAllText(solutionFile, manFixes);
	using (var sln = new Sln(solutionFile, SlnItems.Env)) {
		//var env = new XProjectEnvStub(sln.Result, cfgsln);
		var projItemConfig = new ProjectItemCfg(projects[0], sln.Result.DefaultConfig, sln.Result.ProjectConfigurationPlatforms.First().Value.First());
		//var msProj = new Microsoft.Build.Evaluation.Project(sln.Result.ProjectItems.First().fullPath);
		//var msProj = new Microsoft.Build.Evaluation.Project(Microsoft.Build.Evaluation.NewProjectFileOptions.None);
		//var msProj = Microsoft.Build.Construction.ProjectRootElement.Create(sln.Result.ProjectItems.First().fullPath,Microsoft.Build.Evaluation.NewProjectFileOptions.IncludeAllOptions);
		var msProj = Microsoft.Build.Construction.ProjectRootElement.Create(sln.Result.ProjectItems.First().fullPath, Microsoft.Build.Evaluation.NewProjectFileOptions.None);
		msProj.Sdk = "Microsoft.NET.Sdk";
		var rGroup = msProj.AddPropertyGroup();
		rGroup.AddProperty("OutputType", "EXE");
		rGroup.AddProperty("TargetFramework", "net8.0");
		rGroup.AddProperty("Platforms", "x64");
		msProj.Save();
		//msProj.ItemGroups.First().AddItem();
		//var rGroup = msProj.AddPropertyGroup();
		//msProj.AddProperty("RootNamespace", projects[0].name);
		//var proj = sln.Result.Env.LoadProjects([projItemConfig]).First();
		var proj = sln.Result.Env.LoadProjects().Single();
		//var proj = sln.Result.Env.Projects.Single();
		//sln.Result.Env.AddOrGet(msProj);
		//msProj.AddItem("Compile", @"Program.cs");
		//var proj = sln.Result.Env.LoadProjects([projItemConfig]).First();

		//}
		//using (var sln = new Sln(solutionFile, SlnItems.All)) {
		//var proj = sln.Result.Env.Projects.First();


		foreach (var dll in msg.ReferencedAssemblies) {
			var fInfo = new FileInfo(dll);
			proj.AddReference(fInfo.Name.Substring(0, fInfo.Name.Length - 4), dll, false);
		}
		var refs = proj.GetReferences();
		
		
		//refs.ForEach( imp =>  {
		//	var newProps = new RoProperties<string, net.r_eg.MvsSln.Projects.Item.Metadata>(imp.meta.Where(a=>a.Key.Equals("Private",StringComparison.CurrentCultureIgnoreCase) == false).ToDictionary());
		//	imp.meta = newProps;
		//		}
		//);//nope doesn't work

		//refs.ForEach(a=>a.meta.Remove("Private",out _));
		//proj.SetProperty("Sdk","Microsoft.NET.Sdk");
		proj.Save();

		manFixes = File.ReadAllText(msProj.FullPath);
	manFixes = manFixes.Replace("<Private>False</Private>","");
	File.WriteAllText(msProj.FullPath, manFixes);
@3F
Copy link
Owner

3F commented Apr 13, 2024

Hello,

Sadly the solution had several problems even (like missing global section).

Sorry for the inconvenience. I think the master branch already contains the fixes for the mentioned problems.

I had some other problems in my life, again, while completing a planned release in March (voiced here ~ https://mastodon.social/@github3F/111975646026491387)

Now I'll try to finish 2.7 this April:

  • New configurable LineBuilder to control newline on various platforms; see WProject does not work on Linux #57
  • More fixes around handlers;
  • Modern SlnWriter with async methods including legacy netfx4+;
  • Easy SlnHeader preparing

This is already on dev/2.7 branch with updated tests ! You can also test the latest stable 1abff30 before merge (I didn't open it as PR since in fact I have mostly private reports and local tasks)

@3F
Copy link
Owner

3F commented Apr 13, 2024

ah yes, an easier way to create from scratch (empty map) is planned to review also together with 2.7, I think <_<

@3F
Copy link
Owner

3F commented Apr 26, 2024

@mitchcapper, Is there a reason for trying to remove a Private from added Reference ?

I have checked your code and all the mentioned bugs seem to have been fixed in the upcoming version 2.7.
This or next week, I think, should be released together with the new features through new ISlnWhData and IProjectsToucher implementations that will help to create .sln + project files from scratch, for example (2.7+):

ConfigSln[] slnConf = [new("Debug", "x64")];
ProjectItem[] projects = [new ProjectItem(ProjectType.CsSdk, @$"{projName}\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(solutionFile, hdata))
{
    w.Options = SlnWriterOptions.CreateProjectsIfNotExist;
    w.Write();
}

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

xp.SetProperties(new Dictionary<string, string>()
{
    { "OutputType", "EXE" },
    { "TargetFramework", "net8.0" },
    { "Platforms", "x64" }
});
xp.Save();

the standard (2.x) way is also available, of course, for code from above it can be like:

Dictionary<Type, HandlerValue> whandlers = new()
{
    [typeof(LVisualStudioVersion)] = new(new WVisualStudioVersion(SlnHeader.MakeDefault())),
    [typeof(LProject)] = new(new WProject(projects)),
    [typeof(LProjectConfigurationPlatforms)] = new(new WProjectConfigurationPlatforms(prjConfs)),
    [typeof(LSolutionConfigurationPlatforms)] = new(new WSolutionConfigurationPlatforms(slnConf)),
};

using SlnWriter w = new(solutionFile, whandlers));

I'll edit my example above if anything changes before release.

Note also,

.Replace("TestProj|", "Debug|x64")

This is not a bug. You have incorrect initialization or incorrectly selected ctor (n. formatted instead of configuration + platform)

@mitchcapper
Copy link
Author

Is there a reason for trying to remove a Private from added Reference ?

Create a cleaner output. Wanted to generate the same thing as if one was to manually do it in VS essentially. That way VS also decides in terms of local copy or not.

.Replace("TestProj|", "Debug|x64")
This is not a bug. You have incorrect initialization or incorrectly selected ctor (n. formatted instead of configuration + platform)

Thanks, 100% could be. I was unclear on the right combo of items to get it to be happy, it seemed like a ConfigSln would work there so took it out of the platformData.

New 2.7 style looks great and straightforward!

@3F
Copy link
Owner

3F commented Apr 28, 2024

Create a cleaner output. Wanted to generate the same thing as if one was to manually do it in VS essentially. That way VS also decides in terms of local copy or not.

I added new method signatures through AddReferenceOptions enum that will help to control everything for Reference nodes. The old AddReference() (without AddReferenceOptions) has been marked as obsolete and scheduled to be removed in future versions.

For 2.7 you will be able use HidePrivate flag or some predefined option ~

Default = HideEmbedInteropTypes | HideSpecificVersion,

DefaultResolve = Default
                | ResolveAssemblyName
                | OmitArchitecture
                | OmitCultureNeutral
                | OmitPublicKeyTokenNull,

Mini = Default | HidePrivate,

MiniResolve = Mini | DefaultResolve | OmitCulture,
...

New 2.7 style looks great and straightforward!

MvsSln was based on a pluggable handlers at runtime and map to control everything (ideally).

2.7+ continues this path with new trivial wrappers that prepare data and handlers. This, unfortunately, only applies to the default set at this time, but... maybe 2.8 (well, my priority is to continue 3.0 with IeXod support and completely change IXProject ... someday)

3F added a commit that referenced this issue Apr 29, 2024
* NEW: Added default skeleton for SlnWriter in an attempt to make it easier to create from scratch.
       Empty .Write() methods will use it by default or merge it with the actual Map.

* NEW: Added asynchronous metods* for SlnWriter
       *Both implementations including legacy netfx4.0 target platform, and async/await for modern.

* NEW: Added SMap implementation to wrap ISlnResult.Map. More control over ISection and its handler.

* NEW: Added LhDataHelper as an additional way to prepare default handlers from data (ISlnWhData).
        ```
        LhDataHelper hdata = new();
        hdata.SetHeader(SlnHeader.MakeDefault())
                .SetProjects(projects)
                .SetProjectConfigs(prjConfs)
                .SetSolutionConfigs(slnConf);

        using SlnWriter w = new(solutionFile, hdata);
        ```
        See related issue #61 for details.

* NEW: Implemented CreateProjectsIfNotExist option for SlnWriter.
       ```
       using SlnWriter w = new(solutionFile, hdata);
       w.Options = SlnWriterOptions.CreateProjectsIfNotExist;
       // it will write according ProjectType information (both legacy or sdk-style)
       w.Write();
       ```
       Use +IProjectsToucher to override implementation.
       You can find complete example in #61

* NEW: New modern LineBuilder to make creating new handlers easier or control EOL. Related issue #57.

* NEW: Added DefaultHandlers wrapper to prepare all default according to ISlnResult(ISlnWhData).

* NEW: SlnWriter: + WriteAsString() & WriteAsStringAsync() to save the result as string instead of file.
       ```
       using SlnWriter w = new(handlers);
       string data = await w.WriteAsStringAsync(sln.Result.Map);
       ```

* NEW: Added platform independent IObjHandler.NewLine to specify the EOL for used w\handlers.
       +.UpdateNewLine() extension for a collection of handlers.

* NEW: New modern IXProject.AddReference(.., AddReferenceOptions) to control everything for `Reference` nodes.
       Old signatures has been marked as obsolete and scheduled to be removed in future versions.
       ```
       Default = HideEmbedInteropTypes | HideSpecificVersion,
       DefaultResolve = Default | ResolveAssemblyName | OmitArchitecture | OmitCultureNeutral | ...
       Mini = Default | HidePrivate,
       MiniResolve = Mini | DefaultResolve | OmitCulture,
       ```
       See related issue #61 for details.

* NEW: SlnWriter now supports ISlnResult -> +ISlnWhData data to preapre default handlers itself.

* NEW: Implemented overriding of GetHashCode/Equals for ImportElement, RoProperties, Projects.Item.

* NEW: +IConfPlatform.IsEqualPair(IConfPlatform)

* NEW: ProjectItem adds ability to generate a project name from a specified input path:
       slnDir\ProjectName\src.csproj -> ProjectName
       slnDir\ProjectName.csproj -> ProjectName
       ```
       new ProjectItem(ProjectType.CsSdk, @$"{projName}\src.csproj", slnDir: baseDir)
       ```

* FIXED: Fixed bug with a disappearing `EndProject`. See related #56

* FIXED: Fixed duplicated lines when disabling some features through SlnItems.

* FIXED: Fixed lines being ignored when handlers are not prepared.

* FIXED: If the handler is not initialized as object, the corresponding entry is lost.

* FIXED: Fixed PackageInfo == Equals for null.

* CHANGED: SlnParser has been switched to UTF8 by default.

* CHANGED: SlnWriter now will ignore W\handler that will return null value at IObjHandler.Extract().

* CHANGED: Removed comparing of parent project and origin item in PropertyItem.Equals.

* CHANGED: SlnHeader is sealed now with new ctors and added SlnHeader.MakeDefault().

* CHANGED: ConfigPrj: new ctors +protected internal set for IncludeInBuild and IncludeInDeploy properties.

* CHANGED: Projects.Item and PropertyItem:
           new ctors & renamed evaluatedValue, evaluatedInclude, unevaluatedValue, unevaluatedInclude
           Old access still is available but marked as obsolete and scheduled to be removed in future versions.

* CHANGED: Updated path \ / logic between Windows and unix-like systems.
@3F
Copy link
Owner

3F commented Apr 29, 2024

2.7 is just out; My earlier examples from drafted dev/2.7 are still valid.
Please open another issue if you find something new

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants