side-waffle icon indicating copy to clipboard operation
side-waffle copied to clipboard

No way to replace file or folder names in project templates?

Open AB99 opened this issue 10 years ago • 19 comments

String replacement via the _preprocess.xml files doesn't seem to cause files with the specified key in their file name to be renamed, replacement seems to work on their contents only. If I try to add a ProjectItem element specifying a TargetFileName to the .vstemplate file, I get an error when creating a new project using the template, saying that it cannot find a file with the original file name in a temp directory under AppData. Is there no way then to cause the created files and folders to be renamed?

AB99 avatar Nov 19 '14 19:11 AB99

I also have this need and also get a similar error experience OR just get my projectitem copied to the vstemplate file in the template but the template values created override them since they are last.... example - example of my vstemplate example2 - example of vstemplate in the lstemplate folder.

klabranche avatar Nov 19 '14 19:11 klabranche

OK yeah I'm able to repro it. If the item is in the root directory the merging of the vstemplate file works correctly. For cases where the file is in a sub folder the merging results in duplicates as stated above. This is a bug in CreateTemplateTask.

sayedihashimi avatar Nov 20 '14 08:11 sayedihashimi

OK I've made some changes at https://github.com/ligershark/template-builder/commit/4d0a8001def8456b74e3a0010546e4fefffb790d which I think may address this but I didn't thoroughly test this out. You can get an updated nuget package https://dl.dropboxusercontent.com/u/40134810/SideWaffle/issue188/TemplateBuilder.1.0.3.57-beta.nupkg.zip. Can you try it out and let me know if it works for you?

sayedihashimi avatar Nov 20 '14 10:11 sayedihashimi

That does combine them but then the template fails to create the project stating the file could not be found. The filename is the name before the TargetFileName change. Seems the name of the old file is still being looked for instead of the new TargetFileName later on in the process. I'll try and dig into the source to see if I can find where it should be looking for TargetFileName if it exists (AKA - my best guess of how the template works under the hood)....

For an example if I have the following ProjectItem rule: <ProjectItem ReplaceParameters="true" TargetFileName="$safeprojectname$Context.cs"> XXXXContext.cs</ProjectItem>

The error is it can't find XXXXContext.cs. cannotfind

klabranche avatar Nov 20 '14 15:11 klabranche

@klabranche thanks, ok. It would help if you could get yourself setup to debug templatebuilder so that you can try and make the change and submit a PR. To do that follow the steps below.

  1. Fork TemplateBuilder
  2. clone it locally
  3. Open src\LigerShark.TemplateBuilder.sln in VS and build a debug build
  4. Open a VS command prompt and build your templatepack project passing in /p:ls-TasksRoot=<path-to-debug-folder>

See https://github.com/ligershark/side-waffle/blob/master/build-main.ps1#L272 for an example.

During debugging if you want to make it easy to hit a break point in the code add System.Diagnostics.Debugger.Launch() then when you execute your build a dialog will popup asking if you want to attach VS. Just attach with the instance that has the TemplateBuilder solution open. The bug is in the CreateTemplateTask.cs class. See my previous commit https://github.com/ligershark/template-builder/commit/4d0a8001def8456b74e3a0010546e4fefffb790d.

I'll be traveling for a bit, so I likely won't get to this for a while. The quickest way for us to get this resolved is for us to get your machine setup for debugging and have you submit the PR.

sayedihashimi avatar Nov 20 '14 17:11 sayedihashimi

Will do!

klabranche avatar Nov 20 '14 19:11 klabranche

must be doing something wrong.... Getting a build error....

C:\GitHub\side-waffle>msbuild "TemplatePack\TemplatePack.csproj" /p:VisualStudi oVersion=12.0 /p:TemplateBuilderTargets="..\template-builder\tools\ligershark.te mplates.targets" /p:ls-TasksRoot="..\template-builder\src\LigerShark.TemplateBui lder.Tasks\bin\Debug" /fl

Error:

Project "C:\GitHub\side-waffle\TemplatePack\TemplatePack.csproj" on node 1 (default targets). RestorePackages: "...nuget\NuGet.exe" install "C:\GitHub\side-waffle\TemplatePack\packages.config" -source "" -NonInteractive -RequireConsent -solutionDir "..\ " Restoring NuGet packages... To prevent NuGet from downloading packages during build, open the Visual Studio Options dialog, click on the Package Manager node and uncheck 'Allow NuGet to download missing packages'. All packages listed in packages.config are already installed. Project "C:\GitHub\side-waffle\TemplatePack\TemplatePack.csproj" (1) is building "C:\GitHub\side-waffle\LigerShark.Templates\LigerShark.Templates.csproj" (3) on node 1 (default targets). MainResourcesGeneration: Skipping target "MainResourcesGeneration" because all output files are up-to-date with respect to the input files. CoreResGen: No resources are out of date with respect to their source files. Skipping resource generation. GenerateTargetFrameworkMonikerAttribute: Skipping target "GenerateTargetFrameworkMonikerAttribute" because all output files are up-to-date with respect to the input files. CoreCompile: Skipping target "CoreCompile" because all output files are up-to-date with respect to the input files. _CopyAppConfigFile: Skipping target "_CopyAppConfigFile" because all output files are up-to-date with respect to the input files. CopyFilesToOutputDirectory: LigerShark.Templates -> C:\GitHub\side-waffle\LigerShark.Templates\bin\Debug\LigerShark.Templates.dll Done Building Project "C:\GitHub\side-waffle\LigerShark.Templates\LigerShark.Templates.csproj" (default targets). C:\Program Files (x86)\MSBuild\12.0\bin\Microsoft.Common.CurrentVersion.targets(1697,5): warning MSB3274: The primary reference "C:\GitHub\side-waffle\LigerShark.Templates\bin\Debug\LigerShark.Templates.dll" could not be resolved because it was built against the ".NETFramework,Version=v4.5.1" framework. This is a higher version than the currently targeted framework ".NETFramework,Version=v4.5". [C:\GitHub\side-waffle\TemplatePack\TemplatePack.csproj] C:\GitHub\side-waffle\TemplatePack\ProjectTemplates\Windows\Caliburn.Micro WPF Application\App.xaml(3,14): error MC1000: Unknown build error, ''clr-namespace:$safeprojectname$' mapping URI is not valid. Line 3 Position 14.' [C:\GitHub\side-waffle\TemplatePack\TemplatePack.csproj] C:\GitHub\side-waffle\TemplatePack\ProjectTemplates\Windows\Caliburn.Micro WPF Application\Views\MainView.xaml(1,14): error MC6027: x:Class="$safeprojectname$.Views.MainView" is not valid. '$safeprojectname$.Views.MainView' is not a valid fully qualified class name. Line 1 Position 14. [C:\GitHub\side-waffle\TemplatePack\TemplatePack.csproj] Done Building Project "C:\GitHub\side-waffle\TemplatePack\TemplatePack.csproj" (default targets) -- FAILED.

Build FAILED.

"C:\GitHub\side-waffle\TemplatePack\TemplatePack.csproj" (default target) (1) -> (ResolveAssemblyReferences target) -> C:\Program Files (x86)\MSBuild\12.0\bin\Microsoft.Common.CurrentVersion.targets(1697,5): warning MSB3274: The primary reference "C:\GitHub\side-waffle\LigerShark.Templates\bin\Debug\LigerShark.Templates.dll" could not be resolved because it was built against the ".NETFramework,Version=v4.5.1" framework. This is a higher version than the currently targeted framework ".NETFramework,Version=v4.5". [C:\GitHub\side-waffle\TemplatePack\TemplatePack.csproj]

"C:\GitHub\side-waffle\TemplatePack\TemplatePack.csproj" (default target) (1) -> (MarkupCompilePass1 target) -> C:\GitHub\side-waffle\TemplatePack\ProjectTemplates\Windows\Caliburn.Micro WPF Application\App.xaml(3,14): error MC1000: Unknown build error, ''clr-namespace:$safeprojectname$' mapping URI is not valid. Line 3 Position 14.' [C:\GitHub\side-waffle\TemplatePack\TemplatePack.csproj] C:\GitHub\side-waffle\TemplatePack\ProjectTemplates\Windows\Caliburn.Micro WPF Application\Views\MainView.xaml(1,14): error MC6027: x:Class="$safeprojectname$.Views.MainView" is not valid. '$safeprojectname$.Views.MainView' is not a valid fully qualified class name. Line 1 Position 14. [C:\GitHub\side-waffle\TemplatePack\TemplatePack.csproj]

1 Warning(s)
2 Error(s)

klabranche avatar Nov 20 '14 23:11 klabranche

If you are trying to build SideWaffle.sln itself then do the following.

  1. Clone locally into the same parent folder as SideWaffle
  2. Build a debug version of TB
  3. From PS cmd line execute .\build-main.ps1 -UseLocalTemplateBuilderSrc

Is SideWaffle.sln building in Visual Studio?

sayedihashimi avatar Nov 20 '14 23:11 sayedihashimi

Oh.... I was being very dense. I read templatepack project and was oh...ok.... the templatepack project in sidewaffle instead of YOUR(MY) templatepack project... Sorry about that. I've got the debugger going.

klabranche avatar Nov 21 '14 16:11 klabranche

I think the issue is in the underlying MS wizard/template process not templatebuilder or createtemplatetasks. I think I've been able to replicate the issue in the straight VS template building process as well. I've got to let it go for a bit but will try to replicate again on Monday....

klabranche avatar Nov 21 '14 20:11 klabranche

Ok thanks in that case the best thing to do is file a bug in connect. When you do just submit the vsix and don't mention TemplateBuilder/SideWaffle that's an implementation detail.

sayedihashimi avatar Nov 21 '14 20:11 sayedihashimi

@sayedihashimi Your .57 nuget package you sent me directly is needed to fix the merging of vstemplate and a good change as it allows for someone to for example set up a file to be shown at startup. I.E. - <ProjectItem ReplaceParameters="true" TargetFileName="Project_Readme.html" OpenInWebBrowser="true">Project_Readme.html</ProjectItem>

On the other issue of the file name change: I have confirmed that I can replicate it and fix it following the same steps in both templatebuilder/sidewaffle and the default VS way of building templates, meaning it's not a bug in templatebuilder/sidewaffle.

My goal would be to have my vstemplate where I change the name of a file in the target to have the template process change the name for me. However, If one does not change the name in the csproj to match then the error occurs that the original file is not found. If the change is not dynamic then this is pointless. Just change the name in the template. If one wants to use a dynamic name such as using $safeprojectname$ then, unfortunately, if you change the name in the csproj before packing you end up with a copy error as well. Chicken and egg problem.

The fix is a hack. One must leave the vstemplate and csproj alone when exporting the template. Or in the case of sidewaffle after the VSIX has been built. Then you extract and change the vstemplate and csproj to use the values you desire while leaving the original file alone. In my case for example I was using $safeprojectname$Context.cs as the target name in the vstemplate and in csproj to name the EF CF DB Context to the name of the project by default.

It would be nice to be able to have the process be smart enough to know that a vstemplate name change should copy the original filename into the template THEN change the name and it's csproj reference.... The current result is by design? (misleading in my mind) since the vstemplate has a target name as well as the original file name....

klabranche avatar Nov 24 '14 22:11 klabranche

Created a connect issue for this: https://connect.microsoft.com/VisualStudio/feedback/details/1039473/visual-studio-template-fails-to-find-file-when-targetfilename-is-changed

klabranche avatar Nov 25 '14 18:11 klabranche

Having the same issue, not clearly understand how to fix one. Is it still possible somehow?

avishnyakov avatar Dec 30 '14 15:12 avishnyakov

Nice, found the other way around. With custom IWizard, we can use ProjectFinishedGenerating() override to handle project creation. Having nice project item iterator from here ( http://www.christophdebaene.com/blog/2009/07/08/enumerating-project-items-in-a-visual-studio-solution-2/ ) and the following "renaming" will do. The only hint is to save "Prefix" while pushing a replacementDectionary in "RunStarted()" so that it can be reused in "ProjectFinishedGenerating()".

Hope this helps someone, enjoy!

public void ProjectFinishedGenerating(Project project)
        {
            var it = new ProjectItemIterator(new[] { project }).GetEnumerator();

            while (it.MoveNext())
            {
                var item = it.Current;

                if (!string.IsNullOrEmpty(item.Name))
                {
                    if (item.Name.StartsWith("DR"))
                    {
                       // Prefix is protected variable saved in RunStarted() call
                        item.Name = item.Name.Replace("DR", Prefix);
                    }
                }
            }
        }

avishnyakov avatar Dec 30 '14 15:12 avishnyakov

Is there a generic IWizard that we can add in TemplateBuilder to simplify this?

sayedihashimi avatar Mar 12 '15 03:03 sayedihashimi

Thanks for this, public void ProjectFinishedGenerating(Project project) { var it = new ProjectItemIterator(new[] { project }).GetEnumerator();

        while (it.MoveNext())
        {
            var item = it.Current;

            if (!string.IsNullOrEmpty(item.Name))
            {
                if (item.Name.StartsWith("DR"))
                {
                   // Prefix is protected variable saved in RunStarted() call
                    item.Name = item.Name.Replace("DR", Prefix);
                }
            }
        }
    }

I have one more question, Can we replace content inside .cs file.? For ex. I have Xyz.Dummy.Common namespace and through code should i replace with Xyz.Real.Common. Please share your thoughts on it.

smita-kolhe avatar Aug 02 '17 06:08 smita-kolhe

can any body help me? https://github.com/ligershark/side-waffle/issues/410

kingofday avatar Aug 05 '18 04:08 kingofday

Hi, excuse me if i understand your question wrong. What i do to replace file content is: Let suppose I have 'AssemblyManager_TestProject.cs' file inside 'TestProject.csproj' project, built by project template. Then I had 'AssemblyManager_.....cs' inside project template .ZIP file. The content of this file must be: using System;

namespace Test.$safeprojectname$ {

public class AssemblyManager_$safeprojectname$
{

    public AssemblyManager_$safeprojectname$(){}

}

}

so, when project is created by template and new project name is 'TestProject', content of the file above should be:

using System;

namespace Test.TestProject {

public class AssemblyManager_TestProject
{

    public AssemblyManager_TestProject(){}

}

}

ivanalexandrov74 avatar Aug 30 '18 06:08 ivanalexandrov74