sayedihashimi / template-sample

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Solution template - making sure solution-level items exist in same folder as .sln

Capsup opened this issue · comments

I have a multi-project solution template and with the recent change to 16.10, I can now use solution-type templates and they work great in the .NET CLI.
However, in a Visual Studio host, I run into an issue if the user selects not to have the solution and project file be in the same location, in the Visual Studio creation dialog, because VS generates a new solution file at a folder level above whatever the template specifies.

I have a .editorconfig and nuget.config files that needs to be alongside the .sln to be loaded, but if the above option is chosen, then those files will end up being created in the child folder. If I select the above option, then it works just like it does with the CLI.

Is there any way for me to output those files in the correct location, depending on whatever the user selects in that dialog?

My template.json can be found here

I have the same problem, it would be nice to be able to disable that checkbox.

I just dug around the code for the IDE and noticed there doesn't seem to be anyway to supress the checkbox option from being displayed, even from vstemplate based templates.

We can however detect if that option was checked from within our provider and potentially change behavior based on it.

I'm thinking to get the desired behavior we could have the template specify something in the host file which would do the following regardless of whether the checkbox was checked. If the checkbox was checked, we would not have a change in the current behavior, but if unchecked, we would use the solution file as the project output path and cleanup the generated project folder.

If we would implement this, you would need to explicitly specify the sln file as a primary output, since the IDE created sln would be in the same folder.

Alternatively, we could automatically opt into the above behavior for solution templates. I think in most cases this would be a safe thing to do. If we do that, the host option would opt the template out of this default behavior.

In any case, I think we should only have the new behavior when the template was created as a new Solution, and not via added to an existing solution.

Thoughts?

I'm thinking to get the desired behavior we could have the template specify something in the host file which would do the following regardless of whether the checkbox was checked. If the checkbox was checked, we would not have a change in the current behavior, but if unchecked, we would use the solution file as the project output path and cleanup the generated project folder.

For me, it is not important to suppress that dialog, I think it serves an actual purpose. The most important for me is to have the option of interacting with this primary output solution. Currently, if that is unchecked, there is a folder generated inside the target path, wherein there is a new folder, along with the solution file. Then inside this extra new folder, is the contents of my solution template, except for the actual solution. So if my target path in the dialog is C:\source and name is Foo, I get a folder like:

  • C:\source\Foo
  • C:\source\Foo\Foo.sln
  • C:\source\Foo\Foo\
  • C:\source\Foo\Foo\template files including Foo project

And inside this second Foo folder is my actual solution items.
In the above solution, how would the final folder structure be? Would that additional Foo folder then not be used, and instead, the solution file will be moved into C:\source\Foo\Foo\Foo.sln?

If that's the case, that works for me.

The alternative I see, is to somehow, in the template, specify that I need these additional primary output files, which must be placed alongside whereever the generated .sln ends up.

Or just altogether not have that additional output folder.

I think what we would do for new solution projects only (IsExclusive = true) - not new projects being added to an existing solution we would just always treat this option as being checked by default. This would result in:

c:\source\Foo\Foo.sln (generated by VS)
c:\source\Foo\your outputs here - note that you would want to make sure that your generated sln does not exactly match the sourceName defined in Template.json or else it would overwrite the VS generated file and VS would display a prompt on reloading.

For (isExclusive = false) - we should preserve the existing behavior. In this case the project directory is created under the current sln location and all project output is made there. We should add some sort of property that is passed to the template in this case so that you could suppress certain output files here. In this case, I don't think we should allow the template to modify any files outside its project folder, as we wouldn't want arbitrary files which are not part of the project from being overwritten. It would not be desirable for example for a template which was added to an existing solution to overwrite a preexisting nuget.config.

@phenning if I understand correct, the issue occurs when sourceName and the file name for the solution file matches exactly (minus the extension). Seems like we should add a warning to the analyzer to detect and warn in this case, is that correct?

Looks like the checkbox is still there in VS 2022, although now it doesn't seem to have any effect whether you check or uncheck it. Any updates on whether we can remove it in future?

image

This has been in VS for a long time, and is used by most of the other project types. I don't think it will be removed all up.

@phenning should we ask NPD team to see if we can hide this checkbox?

The checkbox still makes a difference for me on VS22 & .NET 6. Is it not supposed to?

My solution template looks like this:

├── project
│   └── project.csproj
├── README.md
└── solution.sln

With it checked it has the expected output.
Without it checked it still becomes:

├── project
│   ├── project
│   │   └── project.csproj
│   └── README.md
└── solution.sln

Or did I misunderstand the earlier conversation about the checkbox having no effect?

Also see the same as @h3rmanj. Would appreciate a fix for this as it breaks the build scripts in my template to have the solution in a folder one level up.

I'm working on a fix that should address your scenarios, we will expose three properties additional properties via binding.

SoluitionName (The name of the Solution the project is being added to)
IsExclusive (True if being new solution is being created in via NPD, False if adding to existing solution)
CreateSolutionDirectory (Set to True if the above mentioned checkbox is UNCHECKED, False otherwise, including when adding to existing project)

That sounds good. Should be enough for my case at least, to determine what the root of output is, so I can move files afterwards.

Any chance we'll see this soon?

That sounds good. Should be enough for my case at least, to determine what the root of output is, so I can move files afterwards.

Any chance we'll see this soon?

I just committed the change to our main branch for 17.1 Preview 3, so you should see it in the Preview channel when it is released.

Syntax is as follows:

"IsExclusive": {  // you can name the symbol whatever you want, the important thing is the binding below - that must match
   "type": "bind",
   "binding": "context:IsExclusive"
}

You can also specify context:SolutionName and context:CreateSolutionDirectory

Hi! I didn't see the changes mentioned in the 17.1 release notes. Are the variables available yet or did it get pushed back to 17.2?

If you’re asking about IsExclusive, it should be in 17.1

So CreateSolutionDirectory is a new built in symbol that we can check to determine whether the user enabled that setting in the UI. However, how can I prevent the user enabling it in the first place? That setting doesn't make sense for my template and messes up a few relative file paths.

The template provider does not have a way to tell the new project dialog to not display this UI element since the IDE's API surface

The new context properties were implemented to allow the setup of source renames to fix up the relative paths.

Has there been any fix implemented? I'm not too sure what I should be doing with context:CreateSolutionDirectory to fix the issue, as I'm fairly new to the templating.

Also, why am I getting a perfect output when using dotnet new 'template-name' -o WebApi, but when generated in Visual studio, I absolutely need to have the checkbox is checked to get the good directory tree?

We are working on an update to the New Project Dialog to better support this scenario. It will require a small edit to the template.json, I will reply back when I have more info to share.

The new context properties were implemented to allow the setup of source renames to fix up the relative paths.

Has anyone been successful in doing these fixups? I'd be interested in an example.

I've got source renames that fix the paths when CreateSolutionDirectory is true, but the .sln file still contains the old paths. Am I supposed to somehow crack open the solution and fix paths to the csproj files in there, too?

Yes you would need to add the conditions to your sln content as well.

Right, I'm just not sure how to do this (still new at this templating stuff). So let's say we're improving Sayed's solution sample to handle this.

Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SayedHa.Web.MyWeb", "SayedHa.Web.MyWeb\SayedHa.Web.MyWeb.csproj", "{0E62310C-D76A-4681-9926-B1BFFDC379FC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SayedHa.Web.MyClassLibrary", "SayedHa.Web.MyClassLibrary\SayedHa.Web.MyClassLibrary.csproj", "{032123E7-E4E0-4B17-9564-ECA4B57F30B7}"
EndProject

Walk through the VS New Project Dialog (with "Place solution and project in same directory" unchecked) and call the solution "Foo". Now those csproj paths are changed to have the sourceName directory prepended (but these paths are now wrong due to the source rename trick discussed above):

Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Foo.MyWeb", "Foo\Foo.MyWeb\Foo.MyWeb.csproj", "{155C2766-47A0-481A-A88B-9E28F0E0EE0C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Foo.MyClassLibrary", "Foo\Foo.MyClassLibrary\Foo.MyClassLibrary.csproj", "{5F63ED96-7026-4A75-A74B-3CED373AD80E}"
EndProject

...So if I change the template's .sln file to use a condition, what can I do to stop this prepending behavior?

#if (CreateSolutionDirectory)
  ???
#else
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SayedHa.Web.MyWeb", "SayedHa.Web.MyWeb\SayedHa.Web.MyWeb.csproj", "{0E62310C-D76A-4681-9926-B1BFFDC379FC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SayedHa.Web.MyClassLibrary", "SayedHa.Web.MyClassLibrary\SayedHa.Web.MyClassLibrary.csproj", "{032123E7-E4E0-4B17-9564-ECA4B57F30B7}"
EndProject
#endif

@markwaterman with the current behavior of the New Project Dialog it's not possible to get the desired behavior. We have a change to that in Visual Studio 17.5 Preview 1. Download that preview and add the following to the tags in template.json.

  "tags": {
    "language": "C#",
    "type": "solution",
    "editorTreatAs":"solution"
  }

The new item is editorTreatAs here. When you do this, instead of getting the standard experience, you'll get the following dialog.

image

Please give it a shot and let us know if this works for you. It should get into the public build when 17.5 is released.

@sayedihashimi What does the new editorTreatAs setting do apart from change the UI of the dialogue?

@RehanSaeed nothing, it's specific for deciding the behavior of the New Project Dialog. We didn't want to use type=solution because that could break existing templates, so we added this new property. This is only supported in Visual Studio (Windows) currently.

Is this property available in the stable VS release now, or just in preview?

@phenning this is in 17.5 GA right?

I think the issue we're having is that we're launching wizard, so I guess the question is whether there's a way to set the editorTreatAs property in the vstemplate file so that it applies to the wizard template too.

@sayedihashimi I've just got around to testing this (in version 17.6.4) and it works a treat.

The new checkbox "Create in new folder" is much easier to understand. The structure of the project doesn't change based on whether this is checked or not, it simply decides whether to place the project at the root of the selected location or to create it under a sub-folder (if checked).