├── .gitattributes ├── .gitignore ├── AutoRunCustomTool.sln ├── AutoRunCustomTool2015 ├── AutoRunCustomTool2015.csproj └── source.extension.vsixmanifest ├── AutoRunCustomTool2022 ├── AutoRunCustomTool2022.csproj └── source.extension.vsixmanifest ├── AutoRunCustomToolShared ├── AutoRunCustomToolPackage.cs ├── AutoRunCustomToolShared.projitems ├── AutoRunCustomToolShared.shproj ├── GlobalSuppressions.cs ├── Guids.cs ├── Key.snk ├── Properties │ └── AssemblyInfo.cs ├── PropertyExtender.cs ├── PropertyExtenderProvider.cs ├── Resources │ └── Package.ico └── VSPackage.resx ├── README.md ├── Shared └── Resources │ └── Package.ico ├── docs ├── images │ ├── bg_hr.png │ ├── blacktocat.png │ ├── icon_download.png │ └── sprite_download.png ├── index.html ├── javascripts │ └── main.js ├── params.json └── stylesheets │ ├── pygment_trac.css │ └── stylesheet.css ├── img ├── screenshot-annotated-2.0.png └── screenshot-annotated.png └── samples ├── ClassicProject ├── App.config ├── ClassicProject.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── README.md ├── properties.png ├── target.tt ├── target.txt ├── target2.tt ├── target2.txt └── trigger.txt └── SdkStyleProject ├── Program.cs ├── README.md ├── SdkStyleProject.csproj ├── target.tt ├── target.txt ├── target2.tt ├── target2.txt └── trigger.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # MSTest test Results 19 | [Tt]est[Rr]esult*/ 20 | [Bb]uild[Ll]og.* 21 | 22 | *_i.c 23 | *_p.c 24 | *.ilk 25 | *.meta 26 | *.obj 27 | *.pch 28 | *.pdb 29 | *.pgc 30 | *.pgd 31 | *.rsp 32 | *.sbr 33 | *.tlb 34 | *.tli 35 | *.tlh 36 | *.tmp 37 | *.tmp_proj 38 | *.log 39 | *.vspscc 40 | *.vssscc 41 | .builds 42 | *.pidb 43 | *.log 44 | *.scc 45 | 46 | # Visual C++ cache files 47 | ipch/ 48 | *.aps 49 | *.ncb 50 | *.opensdf 51 | *.sdf 52 | *.cachefile 53 | 54 | # Visual Studio profiler 55 | *.psess 56 | *.vsp 57 | *.vspx 58 | 59 | # Guidance Automation Toolkit 60 | *.gpState 61 | 62 | # ReSharper is a .NET coding add-in 63 | _ReSharper*/ 64 | *.[Rr]e[Ss]harper 65 | *.DotSettings 66 | 67 | # TeamCity is a build add-in 68 | _TeamCity* 69 | 70 | # DotCover is a Code Coverage Tool 71 | *.dotCover 72 | 73 | # NCrunch 74 | *.ncrunch* 75 | .*crunch*.local.xml 76 | 77 | # Installshield output folder 78 | [Ee]xpress/ 79 | 80 | # DocProject is a documentation generator add-in 81 | DocProject/buildhelp/ 82 | DocProject/Help/*.HxT 83 | DocProject/Help/*.HxC 84 | DocProject/Help/*.hhc 85 | DocProject/Help/*.hhk 86 | DocProject/Help/*.hhp 87 | DocProject/Help/Html2 88 | DocProject/Help/html 89 | 90 | # Click-Once directory 91 | publish/ 92 | 93 | # Publish Web Output 94 | *.Publish.xml 95 | *.pubxml 96 | 97 | # NuGet Packages Directory 98 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 99 | #packages/ 100 | 101 | # Windows Azure Build Output 102 | csx 103 | *.build.csdef 104 | 105 | # Windows Store app package directory 106 | AppPackages/ 107 | 108 | # Others 109 | sql/ 110 | *.Cache 111 | ClientBin/ 112 | [Ss]tyle[Cc]op.* 113 | ~$* 114 | *~ 115 | *.dbmdl 116 | *.[Pp]ublish.xml 117 | *.pfx 118 | *.publishsettings 119 | 120 | # RIA/Silverlight projects 121 | Generated_Code/ 122 | 123 | # Backup & report files from converting an old project file to a newer 124 | # Visual Studio version. Backup files are not needed, because we have git ;-) 125 | _UpgradeReport_Files/ 126 | Backup*/ 127 | UpgradeLog*.XML 128 | UpgradeLog*.htm 129 | 130 | # SQL Server files 131 | App_Data/*.mdf 132 | App_Data/*.ldf 133 | 134 | 135 | #LightSwitch generated files 136 | GeneratedArtifacts/ 137 | _Pvt_Extensions/ 138 | ModelManifest.xml 139 | 140 | #Roslyn .sln.ide folder 141 | *.sln.ide/ 142 | 143 | # ========================= 144 | # Windows detritus 145 | # ========================= 146 | 147 | # Windows image file caches 148 | Thumbs.db 149 | ehthumbs.db 150 | 151 | # Folder config file 152 | Desktop.ini 153 | 154 | # Recycle Bin used on file shares 155 | $RECYCLE.BIN/ 156 | 157 | # Mac desktop service store files 158 | .DS_Store 159 | 160 | # NuGet packages 161 | 162 | packages/ 163 | 164 | # Visual Studio folder 165 | .vs/ 166 | 167 | -------------------------------------------------------------------------------- /AutoRunCustomTool.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.32112.339 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoRunCustomTool2015", "AutoRunCustomTool2015\AutoRunCustomTool2015.csproj", "{B61D18B8-CDC7-4D5F-A82B-EE6231126ECA}" 7 | EndProject 8 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "AutoRunCustomToolShared", "AutoRunCustomToolShared\AutoRunCustomToolShared.shproj", "{8E0C7164-B1EB-4C6B-8FC2-7D43AB8DDDC2}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoRunCustomTool2022", "AutoRunCustomTool2022\AutoRunCustomTool2022.csproj", "{7A559F3B-752A-4FF6-A29E-E299FC9A8F59}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DA2124EA-9DAA-46FC-9791-9AF6B2BCC5B9}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassicProject", "samples\ClassicProject\ClassicProject.csproj", "{7AAF3CF7-BD0C-4EE8-B9B5-D249B3A098E2}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SdkStyleProject", "samples\SdkStyleProject\SdkStyleProject.csproj", "{99415C7E-B0E4-459C-9432-0260976AC6CA}" 17 | EndProject 18 | Global 19 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 20 | AutoRunCustomToolShared\AutoRunCustomToolShared.projitems*{7a559f3b-752a-4ff6-a29e-e299fc9a8f59}*SharedItemsImports = 4 21 | AutoRunCustomToolShared\AutoRunCustomToolShared.projitems*{8e0c7164-b1eb-4c6b-8fc2-7d43ab8dddc2}*SharedItemsImports = 13 22 | AutoRunCustomToolShared\AutoRunCustomToolShared.projitems*{b61d18b8-cdc7-4d5f-a82b-ee6231126eca}*SharedItemsImports = 4 23 | EndGlobalSection 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Debug|Any CPU = Debug|Any CPU 26 | Release|Any CPU = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {B61D18B8-CDC7-4D5F-A82B-EE6231126ECA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {B61D18B8-CDC7-4D5F-A82B-EE6231126ECA}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {B61D18B8-CDC7-4D5F-A82B-EE6231126ECA}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {B61D18B8-CDC7-4D5F-A82B-EE6231126ECA}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {7A559F3B-752A-4FF6-A29E-E299FC9A8F59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {7A559F3B-752A-4FF6-A29E-E299FC9A8F59}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {7A559F3B-752A-4FF6-A29E-E299FC9A8F59}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {7A559F3B-752A-4FF6-A29E-E299FC9A8F59}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {7AAF3CF7-BD0C-4EE8-B9B5-D249B3A098E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {7AAF3CF7-BD0C-4EE8-B9B5-D249B3A098E2}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {7AAF3CF7-BD0C-4EE8-B9B5-D249B3A098E2}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {7AAF3CF7-BD0C-4EE8-B9B5-D249B3A098E2}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {99415C7E-B0E4-459C-9432-0260976AC6CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {99415C7E-B0E4-459C-9432-0260976AC6CA}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {99415C7E-B0E4-459C-9432-0260976AC6CA}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {99415C7E-B0E4-459C-9432-0260976AC6CA}.Release|Any CPU.Build.0 = Release|Any CPU 45 | EndGlobalSection 46 | GlobalSection(SolutionProperties) = preSolution 47 | HideSolutionNode = FALSE 48 | EndGlobalSection 49 | GlobalSection(NestedProjects) = preSolution 50 | {7AAF3CF7-BD0C-4EE8-B9B5-D249B3A098E2} = {DA2124EA-9DAA-46FC-9791-9AF6B2BCC5B9} 51 | {99415C7E-B0E4-459C-9432-0260976AC6CA} = {DA2124EA-9DAA-46FC-9791-9AF6B2BCC5B9} 52 | EndGlobalSection 53 | GlobalSection(ExtensibilityGlobals) = postSolution 54 | SolutionGuid = {52FF6476-B56C-48C1-8F5C-02B9ADC6A718} 55 | EndGlobalSection 56 | EndGlobal 57 | -------------------------------------------------------------------------------- /AutoRunCustomTool2015/AutoRunCustomTool2015.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 15.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | Debug 10 | AnyCPU 11 | 2.0 12 | {B61D18B8-CDC7-4D5F-A82B-EE6231126ECA} 13 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | Library 15 | Properties 16 | ThomasLevesque.AutoRunCustomTool2015 17 | AutoRunCustomTool2015 18 | True 19 | ../AutoRunCustomToolShared/Key.snk 20 | v4.5.2 21 | 22 | 23 | true 24 | full 25 | false 26 | bin\Debug\ 27 | DEBUG;TRACE 28 | prompt 29 | 4 30 | false 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | true 40 | false 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Designer 51 | 52 | 53 | 54 | 55 | 10.0.3 56 | 57 | 58 | 14.3.25407 59 | 60 | 61 | 14.3.26929 62 | 63 | 64 | 14.0.25030 65 | 66 | 67 | 68 | 69 | Package.ico 70 | true 71 | Always 72 | 73 | 74 | 75 | 76 | true 77 | 78 | 79 | 80 | 87 | -------------------------------------------------------------------------------- /AutoRunCustomTool2015/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | AutoRunCustomTool for Visual Studio 2015/2017/2019 6 | Automatically runs the custom tool on another file when a file is modified. 7 | https://github.com/thomaslevesque/AutoRunCustomTool 8 | Package.ico 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /AutoRunCustomTool2022/AutoRunCustomTool2022.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 17.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | Debug 10 | AnyCPU 11 | 2.0 12 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 13 | {7A559F3B-752A-4FF6-A29E-E299FC9A8F59} 14 | Library 15 | Properties 16 | AutoRunCustomTool2022 17 | AutoRunCustomTool2022 18 | true 19 | ../AutoRunCustomToolShared/Key.snk 20 | v4.7.2 21 | true 22 | true 23 | true 24 | false 25 | false 26 | true 27 | true 28 | Program 29 | $(DevEnvDir)devenv.exe 30 | /rootsuffix Exp 31 | 32 | 33 | true 34 | full 35 | false 36 | bin\Debug\ 37 | DEBUG;TRACE 38 | prompt 39 | 4 40 | 41 | 42 | pdbonly 43 | true 44 | bin\Release\ 45 | TRACE 46 | prompt 47 | 4 48 | 49 | 50 | 51 | Designer 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | Package.ico 66 | Always 67 | true 68 | 69 | 70 | 71 | 72 | 73 | 80 | -------------------------------------------------------------------------------- /AutoRunCustomTool2022/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AutoRunCustomTool for Visual Studio 2022 6 | Automatically runs the custom tool on another file when a file is modified. 7 | https://github.com/thomaslevesque/AutoRunCustomTool 8 | Package.ico 9 | 10 | 11 | 12 | amd64 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /AutoRunCustomToolShared/AutoRunCustomToolPackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using EnvDTE; 8 | using Microsoft.VisualStudio; 9 | using Microsoft.VisualStudio.Shell.Interop; 10 | using Microsoft.VisualStudio.Shell; 11 | using VSLangProj; 12 | using IExtenderProvider = EnvDTE.IExtenderProvider; 13 | using System.Threading; 14 | using System.Threading.Tasks; 15 | 16 | namespace ThomasLevesque.AutoRunCustomTool 17 | { 18 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 19 | [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] 20 | [Guid(GuidList.guidAutoRunCustomToolPkgString)] 21 | [ProvideAutoLoad(UIContextGuids.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)] 22 | public sealed class AutoRunCustomToolPackage : AsyncPackage 23 | { 24 | public AutoRunCustomToolPackage() 25 | { 26 | Debug.WriteLine("Entering constructor for: {0}", this); 27 | } 28 | 29 | #region Package Members 30 | 31 | private DTE _dte; 32 | private Events _events; 33 | private DocumentEvents _documentEvents; 34 | private OutputWindowPane _outputPane; 35 | private ErrorListProvider _errorListProvider; 36 | private readonly Dictionary _registerExtenderProviders = new Dictionary(); 37 | 38 | public const string TargetsPropertyName = "RunCustomToolOn"; 39 | 40 | void RegisterExtenderProvider() 41 | { 42 | var provider = new PropertyExtenderProvider(_dte, this); 43 | string name = PropertyExtenderProvider.ExtenderName; 44 | RegisterExtenderProvider(VSConstants.CATID.CSharpFileProperties_string, name, provider); 45 | RegisterExtenderProvider(VSConstants.CATID.VBFileProperties_string, name, provider); 46 | } 47 | 48 | void RegisterExtenderProvider(string extenderCatId, string name, IExtenderProvider extenderProvider) 49 | { 50 | int cookie = _dte.ObjectExtenders.RegisterExtenderProvider(extenderCatId, name, extenderProvider); 51 | _registerExtenderProviders.Add(cookie, extenderProvider); 52 | } 53 | 54 | void DocumentEvents_DocumentSaved(Document doc) 55 | { 56 | var docItem = doc.ProjectItem; 57 | if (docItem == null) 58 | return; 59 | 60 | string docFullPath = (string)GetPropertyValue(docItem, "FullPath"); 61 | 62 | var projectName = docItem.ContainingProject.UniqueName; 63 | IVsSolution solution = (IVsSolution)GetGlobalService(typeof(SVsSolution)); 64 | IVsHierarchy project; 65 | solution.GetProjectOfUniqueName(projectName, out project); 66 | 67 | var docErrors = _errorListProvider.Tasks.Cast().Where(t => t.Document == docFullPath).ToList(); 68 | foreach (var errorTask in docErrors) 69 | { 70 | _errorListProvider.Tasks.Remove(errorTask); 71 | } 72 | 73 | var targets = new List(); 74 | 75 | string customTool = GetPropertyValue(docItem, "CustomTool") as string; 76 | if (customTool == "AutoRunCustomTool") 77 | { 78 | LogWarning(project, docFullPath, "Setting Custom Tool to 'AutoRunCustomTool' is still supported for compatibility, but is deprecated. Use the 'Run custom tool on' property instead"); 79 | string targetName = GetPropertyValue(docItem, "CustomToolNamespace") as string; 80 | if (string.IsNullOrEmpty(targetName)) 81 | { 82 | LogError(project, docFullPath, "The target file is not specified. Enter its relative path in the 'Custom tool namespace' property"); 83 | return; 84 | } 85 | targets.Add(targetName); 86 | } 87 | else 88 | { 89 | IVsBuildPropertyStorage storage = project as IVsBuildPropertyStorage; 90 | if (storage == null) 91 | return; 92 | 93 | uint itemId; 94 | if (project.ParseCanonicalName(docFullPath, out itemId) != 0) 95 | return; 96 | 97 | string runCustomToolOn; 98 | if (storage.GetItemAttribute(itemId, TargetsPropertyName, out runCustomToolOn) != 0) 99 | return; 100 | 101 | if (runCustomToolOn == null) 102 | return; 103 | targets.AddRange(runCustomToolOn.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); 104 | } 105 | 106 | foreach (var targetName in targets) 107 | { 108 | string dir = Path.GetDirectoryName(docFullPath); 109 | // ReSharper disable once AssignNullToNotNullAttribute 110 | string targetPath = Path.GetFullPath(Path.Combine(dir, targetName)); 111 | var targetItem = _dte.Solution.FindProjectItem(targetPath); 112 | if (targetItem == null) 113 | { 114 | LogError(project, docFullPath, "Target item '{0}' was not found", targetPath); 115 | continue; 116 | } 117 | 118 | string targetCustomTool = (string)GetPropertyValue(targetItem, "CustomTool"); 119 | if (string.IsNullOrEmpty(targetCustomTool)) 120 | { 121 | LogError(project, docFullPath, "Target item '{0}' doesn't define a custom tool", targetPath); 122 | continue; 123 | } 124 | 125 | var vsTargetItem = (VSProjectItem)targetItem.Object; 126 | LogActivity("Running custom tool on '{0}'", targetPath); 127 | vsTargetItem.RunCustomTool(); 128 | } 129 | } 130 | 131 | protected override async System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 132 | { 133 | Debug.WriteLine("Entering Initialize() of: {0}", this); 134 | await base.InitializeAsync(cancellationToken, progress); 135 | _dte = (DTE)(await GetServiceAsync(typeof(DTE))); 136 | _events = _dte.Events; 137 | _documentEvents = _events.DocumentEvents; 138 | _documentEvents.DocumentSaved += DocumentEvents_DocumentSaved; 139 | 140 | var window = _dte.Windows.Item(EnvDTE.Constants.vsWindowKindOutput); 141 | 142 | var outputWindow = (OutputWindow)window.Object; 143 | 144 | _outputPane = outputWindow.OutputWindowPanes 145 | .Cast() 146 | .FirstOrDefault(p => p.Name == "AutoRunCustomTool") 147 | ?? outputWindow.OutputWindowPanes.Add("AutoRunCustomTool"); 148 | _errorListProvider = new ErrorListProvider(this) 149 | { 150 | ProviderName = "AutoRunCustomTool", 151 | ProviderGuid = Guid.NewGuid() 152 | }; 153 | RegisterExtenderProvider(); 154 | } 155 | 156 | private void LogActivity(string format, params object[] args) 157 | { 158 | _outputPane.Activate(); 159 | _outputPane.OutputString(string.Format(format, args) + Environment.NewLine); 160 | } 161 | 162 | private void LogError(IVsHierarchy project, string document, string format, params object[] args) 163 | { 164 | string text = string.Format(format, args); 165 | LogErrorTask(project, document, TaskErrorCategory.Error, text); 166 | } 167 | 168 | private void LogWarning(IVsHierarchy project, string document, string format, params object[] args) 169 | { 170 | string text = string.Format(format, args); 171 | LogErrorTask(project, document, TaskErrorCategory.Warning, text); 172 | } 173 | 174 | private void LogErrorTask(IVsHierarchy project, string document, TaskErrorCategory errorCategory, string text) 175 | { 176 | var task = new ErrorTask 177 | { 178 | Category = TaskCategory.BuildCompile, 179 | ErrorCategory = errorCategory, 180 | Text = "AutoRunCustomTool: " + text, 181 | Document = document, 182 | HierarchyItem = project, 183 | Line = -1, 184 | Column = -1 185 | }; 186 | _errorListProvider.Tasks.Add(task); 187 | string prefix = ""; 188 | switch (errorCategory) 189 | { 190 | case TaskErrorCategory.Error: 191 | prefix = "Error: "; 192 | break; 193 | case TaskErrorCategory.Warning: 194 | prefix = "Warning: "; 195 | break; 196 | } 197 | _outputPane.OutputString(prefix + text + Environment.NewLine); 198 | } 199 | 200 | private static object GetPropertyValue(ProjectItem item, object index) 201 | { 202 | try 203 | { 204 | var prop = item.Properties.Item(index); 205 | if (prop != null) 206 | return prop.Value; 207 | } 208 | catch (ArgumentException) { } 209 | return null; 210 | } 211 | 212 | #endregion 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /AutoRunCustomToolShared/AutoRunCustomToolShared.projitems: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | 8e0c7164-b1eb-4c6b-8fc2-7d43ab8dddc2 7 | 8 | 9 | AutoRunCustomToolShared 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Designer 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /AutoRunCustomToolShared/AutoRunCustomToolShared.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8e0c7164-b1eb-4c6b-8fc2-7d43ab8dddc2 5 | 14.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /AutoRunCustomToolShared/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. Project-level 3 | // suppressions either have no target or are given a specific target 4 | // and scoped to a namespace, type, member, etc. 5 | // 6 | // To add a suppression to this file, right-click the message in the 7 | // Error List, point to "Suppress Message(s)", and click "In Project 8 | // Suppression File". You do not need to add suppressions to this 9 | // file manually. 10 | 11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")] 12 | -------------------------------------------------------------------------------- /AutoRunCustomToolShared/Guids.cs: -------------------------------------------------------------------------------- 1 | // Guids.cs 2 | // MUST match guids.h 3 | using System; 4 | 5 | namespace ThomasLevesque.AutoRunCustomTool 6 | { 7 | static class GuidList 8 | { 9 | public const string guidAutoRunCustomToolPkgString = "7ec0f89c-f00d-48ab-a76b-713d90fdbf03"; 10 | public const string guidAutoRunCustomToolCmdSetString = "0c95b506-373b-43f9-b98b-0fa389e08a6f"; 11 | 12 | public static readonly Guid guidAutoRunCustomToolCmdSet = new Guid(guidAutoRunCustomToolCmdSetString); 13 | }; 14 | } -------------------------------------------------------------------------------- /AutoRunCustomToolShared/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/AutoRunCustomTool/4cb0123bf9f4b2f39b033861d0413f50290c88ff/AutoRunCustomToolShared/Key.snk -------------------------------------------------------------------------------- /AutoRunCustomToolShared/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Resources; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("AutoRunCustomTool")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("Thomas Levesque")] 14 | [assembly: AssemblyProduct("AutoRunCustomTool")] 15 | [assembly: AssemblyCopyright("")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | [assembly: ComVisible(false)] 19 | [assembly: CLSCompliant(false)] 20 | [assembly: NeutralResourcesLanguage("en-US")] 21 | 22 | [assembly: AssemblyVersion("3.1.0.0")] 23 | [assembly: AssemblyFileVersion("3.1.0.0")] 24 | 25 | [assembly: InternalsVisibleTo("AutoRunCustomTool_IntegrationTests, PublicKey=00240000048000009400000006020000002400005253413100040000010001002d5afe9769cb55b94316099caf879e318f22c86b1d448f9ec8dbb7c67d52368bdde6ff6fb4e13372b9f839915260910ab3db617e2ebedf5f0816b2e2629c9003f31efb2ca730aa6b87fb3986ba4881dc6c9f957ad52186a82f5d5073f30086d1ee2a615572963e7ccda405b0cb4319cefb676f490f0d7e326173465c8ff7898a")] 26 | [assembly: InternalsVisibleTo("AutoRunCustomTool_UnitTests, PublicKey=00240000048000009400000006020000002400005253413100040000010001002d5afe9769cb55b94316099caf879e318f22c86b1d448f9ec8dbb7c67d52368bdde6ff6fb4e13372b9f839915260910ab3db617e2ebedf5f0816b2e2629c9003f31efb2ca730aa6b87fb3986ba4881dc6c9f957ad52186a82f5d5073f30086d1ee2a615572963e7ccda405b0cb4319cefb676f490f0d7e326173465c8ff7898a")] 27 | -------------------------------------------------------------------------------- /AutoRunCustomToolShared/PropertyExtender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Diagnostics; 4 | using System.Drawing.Design; 5 | using System.Runtime.InteropServices; 6 | using EnvDTE; 7 | using Microsoft.VisualStudio.Shell.Interop; 8 | 9 | namespace ThomasLevesque.AutoRunCustomTool 10 | { 11 | [ComVisible(true)] 12 | public class PropertyExtender 13 | { 14 | private readonly IVsBuildPropertyStorage _storage; 15 | private readonly uint _itemId; 16 | private readonly IExtenderSite _extenderSite; 17 | private readonly int _cookie; 18 | 19 | public PropertyExtender(IVsBuildPropertyStorage storage, uint itemId, IExtenderSite extenderSite, int cookie) 20 | { 21 | _storage = storage; 22 | _itemId = itemId; 23 | _extenderSite = extenderSite; 24 | _cookie = cookie; 25 | } 26 | 27 | ~PropertyExtender() 28 | { 29 | try 30 | { 31 | if (_extenderSite != null) 32 | _extenderSite.NotifyDelete(_cookie); 33 | } 34 | catch(Exception ex) 35 | { 36 | Debug.WriteLine("Error in PropertyExtender finalizer: {0}", ex); 37 | } 38 | } 39 | 40 | [Category("AutoRunCustomTool")] 41 | [DisplayName("Run custom tool on")] 42 | [Description("When this file is saved, the custom tool will be run on the files listed in this field")] 43 | [Editor("System.Windows.Forms.Design.StringArrayEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))] 44 | public string[] RunCustomToolOn 45 | { 46 | get 47 | { 48 | return LoadRunCustomToolOn(); 49 | } 50 | set 51 | { 52 | SaveRunCustomToolOn(value); 53 | } 54 | } 55 | 56 | private string[] LoadRunCustomToolOn() 57 | { 58 | string s; 59 | _storage.GetItemAttribute(_itemId, AutoRunCustomToolPackage.TargetsPropertyName, out s); 60 | if (s != null) 61 | { 62 | return s.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); 63 | } 64 | return null; 65 | } 66 | 67 | private void SaveRunCustomToolOn(string[] items) 68 | { 69 | string s = null; 70 | if (items != null) 71 | { 72 | s = string.Join(";", items); 73 | } 74 | _storage.SetItemAttribute(_itemId, AutoRunCustomToolPackage.TargetsPropertyName, s); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /AutoRunCustomToolShared/PropertyExtenderProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using EnvDTE; 4 | using Microsoft.VisualStudio.Shell.Interop; 5 | 6 | namespace ThomasLevesque.AutoRunCustomTool 7 | { 8 | [ComVisible(true)] 9 | [Guid(ExtenderGuid)] 10 | public class PropertyExtenderProvider : IExtenderProvider 11 | { 12 | public const string ExtenderName = "AutoRunCustomTool.PropertyExtenderProvider"; 13 | public const string ExtenderGuid = "124D1A83-20C0-4783-AD6B-032929BEC4B0"; 14 | 15 | private readonly DTE _dte; 16 | private readonly IServiceProvider _serviceProvider; 17 | 18 | public PropertyExtenderProvider(DTE dte, IServiceProvider serviceProvider) 19 | { 20 | _dte = dte; 21 | _serviceProvider = serviceProvider; 22 | } 23 | 24 | public object GetExtender(string extenderCATID, string extenderName, object extendeeObject, IExtenderSite extenderSite, int cookie) 25 | { 26 | dynamic extendee = extendeeObject; 27 | string fullPath = extendee.FullPath; 28 | var projectItem = _dte.Solution.FindProjectItem(fullPath); 29 | IVsSolution solution = (IVsSolution) _serviceProvider.GetService(typeof(SVsSolution)); 30 | IVsHierarchy projectHierarchy; 31 | if (solution.GetProjectOfUniqueName(projectItem.ContainingProject.UniqueName, out projectHierarchy) != 0) 32 | return null; 33 | uint itemId; 34 | if (projectHierarchy.ParseCanonicalName(fullPath, out itemId) != 0) 35 | return null; 36 | 37 | return new PropertyExtender((IVsBuildPropertyStorage) projectHierarchy, itemId, extenderSite, cookie); 38 | } 39 | 40 | public bool CanExtend(string extenderCATID, string extenderName, object extendeeObject) 41 | { 42 | return true; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /AutoRunCustomToolShared/Resources/Package.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/AutoRunCustomTool/4cb0123bf9f4b2f39b033861d0413f50290c88ff/AutoRunCustomToolShared/Resources/Package.ico -------------------------------------------------------------------------------- /AutoRunCustomToolShared/VSPackage.resx: -------------------------------------------------------------------------------- 1 |  2 | 12 | 13 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | text/microsoft-resx 120 | 121 | 122 | 2.0 123 | 124 | 125 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 126 | 127 | 128 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 129 | 130 | 131 | 132 | AutoRunCustomTool 133 | 134 | 135 | Automatically runs the custom tool on another file when a file is modified. 136 | 137 | 138 | Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 139 | 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AutoRunCustomTool 2 | ================= 3 | 4 | > [!WARNING] 5 | > This project is no longer maintained. The VS extension remains available as-is on the Visual Studio marketplace, but no new version will be published. 6 | > 7 | > Some of the reasons for abandoning this project are: 8 | > - It doesn't fully work with SDK-style projects (i.e. non-legacy projects), which means it's becoming irrelevant 9 | > - I no longer use this extension myself 10 | > - I switched to Rider a few years ago, and I rarely, if ever, use Visual Studio 11 | > - The main use case for this extension, T4 templates, is becoming obsolete. .NET now offers better, built-in approaches for code generation. 12 | > 13 | > Feel free to fork the project if you need to make some changes. 14 | 15 | *Download from Visual Studio Marketplace: [VS 2015/2017/2019](https://marketplace.visualstudio.com/items?itemName=ThomasLevesque.AutoRunCustomTool), [VS 2022](https://marketplace.visualstudio.com/items?itemName=ThomasLevesque.AutoRunCustomTool2022)* 16 | 17 | > [!CAUTION] 18 | > **Known issue**: This extension doesn't fully work with SDK-style (.NET Core / .NET 5+) projects. It's usable, but without UI support; you'll need to manually set the `RunCustomToolOn` property in the project file. See [#18](https://github.com/thomaslevesque/AutoRunCustomTool/issues/18#issuecomment-1053609437) for details. 19 | 20 | A Visual Studio extension that automatically runs the custom tool on another file when a file is modified 21 | 22 | OK, I realize that this description is perhaps not very clear, so let's take an example. Let's assume (well, it's not really an assumption; it's actually the scenario that caused me to create this tool) that in your project, you can't (or don't want to) use the default custom tool for resx resource files. Instead, you use a T4 template to generate the code associated with the resx file. So you have 3 files : 23 | 24 | - *Strings.resx*, that contains the actual string resources 25 | - *Strings.tt*, a T4 templates that generates code to access the resources from *Strings.resx* 26 | - *Strings.cs*, a C# source file that is generated by *Strings.tt* based on the content of *Strings.resx* 27 | 28 | The problem with this setup is that Visual Studio doesn't know that a change to *Strings.resx* should cause a regeneration of *Strings.cs*, so when you just added a resource to the resx file, it's not immediately available to your code, and doesn't appear in Intellisense. You have to right-click *Strings.tt* and select "Run custom tool" so that the template is run again. This is *very* annoying... 29 | 30 | This VS extension solves the problem very simply: when it detects a change to the "trigger" file (*Strings.resx*), it reruns the custom tool for the "target" file (*Strings.tt*). 31 | 32 | Note that this was just an example; the trigger file doesn't have to be a resx file, and the "target" file doesn't have to be a T4 template. Both can be anything you want, as long as the target has a custom tool defined. 33 | 34 | 35 | How to use 36 | ---------- 37 | 38 | After you install the extension, you should see a new **Run custom tool on** property on each project item. Just edit this property to add the name(s) of the target file(s). That's it! 39 | 40 | Here's a screenshot that shows how it works: 41 | 42 | ![AutoRunCustomTool demo](img/screenshot-annotated-2.0.png) 43 | 44 | **Note to users of previous versions**: in previous versions, you had to set the *Custom Tool* property for the trigger file to "AutoRunCustomTool", and set the *Custom tool namespace* to the name of the target file. This sometimes caused a warning in Visual Studio : "Cannot find custom tool 'AutoRunCustomTool' on this system.". I changed the way the extension works so that it doesn't use the *Custom Tool* property anymore. To ensure compatibility with existing projects, the *Custom Tool* approach is still supported, but not recommended. 45 | -------------------------------------------------------------------------------- /Shared/Resources/Package.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/AutoRunCustomTool/4cb0123bf9f4b2f39b033861d0413f50290c88ff/Shared/Resources/Package.ico -------------------------------------------------------------------------------- /docs/images/bg_hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/AutoRunCustomTool/4cb0123bf9f4b2f39b033861d0413f50290c88ff/docs/images/bg_hr.png -------------------------------------------------------------------------------- /docs/images/blacktocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/AutoRunCustomTool/4cb0123bf9f4b2f39b033861d0413f50290c88ff/docs/images/blacktocat.png -------------------------------------------------------------------------------- /docs/images/icon_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/AutoRunCustomTool/4cb0123bf9f4b2f39b033861d0413f50290c88ff/docs/images/icon_download.png -------------------------------------------------------------------------------- /docs/images/sprite_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/AutoRunCustomTool/4cb0123bf9f4b2f39b033861d0413f50290c88ff/docs/images/sprite_download.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | AutoRunCustomTool 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | View on GitHub 20 | 21 |

AutoRunCustomTool

22 |

A Visual Studio extension that automatically runs the custom tool on another file when a file is modified

23 | 24 |
25 | Download this project as a .zip file 26 | Download this project as a tar.gz file 27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 |

35 | AutoRunCustomTool

36 | 37 |

Download from Visual Studio Gallery

38 | 39 |

A Visual Studio extension that automatically runs the custom tool on another file when a file is modified

40 | 41 |

OK, I realize that this description is perhaps not very clear, so let's take an example. Let's assume (well, it's not really an assumption; it's actually the scenario that caused me to create this tool) that in your project, you can't (or don't want to) use the default custom tool for resx resource files. Instead, you use a T4 template to generate the code associated with the resx file. So you have 3 files :

42 | 43 |
    44 |
  • 45 | Strings.resx, that contains the actual string resources
  • 46 |
  • 47 | Strings.tt, a T4 templates that generates code to access the resources from Strings.resx 48 | 49 |
      50 |
    • 51 | Strings.cs, a C# source file that is generated by Strings.tt based on the content of Strings.resx 52 |
    • 53 |
    54 |
  • 55 |

The problem with this setup is that Visual Studio doesn't know that a change to Strings.resx should cause a regeneration of Strings.cs, so when you just added a resource to the resx file, it's not immediately available to your code, and doesn't appear in Intellisense. You have to right-click Strings.tt and select "Run custom tool" so that the template is run again. This is very annoying...

56 | 57 |

This VS extension solves the problem very simply: when it detects a change to the "trigger" file (Strings.resx), it reruns the custom tool for the "target" file (Strings.tt).

58 | 59 |

Note that this was just an example; the trigger file doesn't have to be a resx file, and the "target" file doesn't have to be a T4 template. Both can be anything you want, as long as the target has a custom tool defined.

60 | 61 |

62 | How to use

63 | 64 |

After you install the extension, you should see a new Run custom tool on property on each project item. Just edit this property to add the name(s) of the target file(s). That's it!

65 | 66 |

Here's a screenshot that shows how it works:

67 | 68 |

AutoRunCustomTool demo

69 | 70 |

Note to users of previous versions: in previous versions, you had to set the Custom Tool property for the trigger file to "AutoRunCustomTool", and set the Custom tool namespace to the name of the target file. This sometimes caused a warning in Visual Studio : "Cannot find custom tool 'AutoRunCustomTool' on this system.". I changed the way the extension works so that it doesn't use the Custom Tool property anymore. To ensure compatibility with existing projects, the Custom Tool approach is still supported, but not recommended.

71 |
72 |
73 | 74 | 75 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /docs/javascripts/main.js: -------------------------------------------------------------------------------- 1 | console.log('This would be the main JS file.'); 2 | -------------------------------------------------------------------------------- /docs/params.json: -------------------------------------------------------------------------------- 1 | {"name":"AutoRunCustomTool","tagline":"A Visual Studio extension that automatically runs the custom tool on another file when a file is modified","body":"AutoRunCustomTool\r\n=================\r\n\r\n*[Download from Visual Studio Gallery](http://visualstudiogallery.msdn.microsoft.com/ecb123bf-44bb-4ae3-91ee-a08fc1b9770e)*\r\n\r\nA Visual Studio extension that automatically runs the custom tool on another file when a file is modified\r\n\r\nOK, I realize that this description is perhaps not very clear, so let's take an example. Let's assume (well, it's not really an assumption; it's actually the scenario that caused me to create this tool) that in your project, you can't (or don't want to) use the default custom tool for resx resource files. Instead, you use a T4 template to generate the code associated with the resx file. So you have 3 files :\r\n\r\n- *Strings.resx*, that contains the actual string resources\r\n- *Strings.tt*, a T4 templates that generates code to access the resources from *Strings.resx*\r\n - *Strings.cs*, a C# source file that is generated by *Strings.tt* based on the content of *Strings.resx*\r\n\r\nThe problem with this setup is that Visual Studio doesn't know that a change to *Strings.resx* should cause a regeneration of *Strings.cs*, so when you just added a resource to the resx file, it's not immediately available to your code, and doesn't appear in Intellisense. You have to right-click *Strings.tt* and select \"Run custom tool\" so that the template is run again. This is *very* annoying...\r\n\r\nThis VS extension solves the problem very simply: when it detects a change to the \"trigger\" file (*Strings.resx*), it reruns the custom tool for the \"target\" file (*Strings.tt*).\r\n\r\nNote that this was just an example; the trigger file doesn't have to be a resx file, and the \"target\" file doesn't have to be a T4 template. Both can be anything you want, as long as the target has a custom tool defined.\r\n\r\n\r\nHow to use\r\n----------\r\n\r\nAfter you install the extension, you should see a new **Run custom tool on** property on each project item. Just edit this property to add the name(s) of the target file(s). That's it!\r\n\r\nHere's a screenshot that shows how it works:\r\n\r\n![AutoRunCustomTool demo](https://raw.githubusercontent.com/thomaslevesque/AutoRunCustomTool/master/img/screenshot-annotated-2.0.png)\r\n\r\n**Note to users of previous versions**: in previous versions, you had to set the *Custom Tool* property for the trigger file to \"AutoRunCustomTool\", and set the *Custom tool namespace* to the name of the target file. This sometimes caused a warning in Visual Studio : \"Cannot find custom tool 'AutoRunCustomTool' on this system.\". I changed the way the extension works so that it doesn't use the *Custom Tool* property anymore. To ensure compatibility with existing projects, the *Custom Tool* approach is still supported, but not recommended.\r\n","google":"","note":"Don't delete this file! It's used internally to help with page regeneration."} -------------------------------------------------------------------------------- /docs/stylesheets/pygment_trac.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #f0f3f3; } 3 | .highlight .c { color: #0099FF; font-style: italic } /* Comment */ 4 | .highlight .err { color: #AA0000; background-color: #FFAAAA } /* Error */ 5 | .highlight .k { color: #006699; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #555555 } /* Operator */ 7 | .highlight .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */ 8 | .highlight .cp { color: #009999 } /* Comment.Preproc */ 9 | .highlight .c1 { color: #0099FF; font-style: italic } /* Comment.Single */ 10 | .highlight .cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */ 11 | .highlight .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 14 | .highlight .gh { color: #003300; font-weight: bold } /* Generic.Heading */ 15 | .highlight .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */ 16 | .highlight .go { color: #AAAAAA } /* Generic.Output */ 17 | .highlight .gp { color: #000099; font-weight: bold } /* Generic.Prompt */ 18 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 19 | .highlight .gu { color: #003300; font-weight: bold } /* Generic.Subheading */ 20 | .highlight .gt { color: #99CC66 } /* Generic.Traceback */ 21 | .highlight .kc { color: #006699; font-weight: bold } /* Keyword.Constant */ 22 | .highlight .kd { color: #006699; font-weight: bold } /* Keyword.Declaration */ 23 | .highlight .kn { color: #006699; font-weight: bold } /* Keyword.Namespace */ 24 | .highlight .kp { color: #006699 } /* Keyword.Pseudo */ 25 | .highlight .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #007788; font-weight: bold } /* Keyword.Type */ 27 | .highlight .m { color: #FF6600 } /* Literal.Number */ 28 | .highlight .s { color: #CC3300 } /* Literal.String */ 29 | .highlight .na { color: #330099 } /* Name.Attribute */ 30 | .highlight .nb { color: #336666 } /* Name.Builtin */ 31 | .highlight .nc { color: #00AA88; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #336600 } /* Name.Constant */ 33 | .highlight .nd { color: #9999FF } /* Name.Decorator */ 34 | .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ 35 | .highlight .ne { color: #CC0000; font-weight: bold } /* Name.Exception */ 36 | .highlight .nf { color: #CC00FF } /* Name.Function */ 37 | .highlight .nl { color: #9999FF } /* Name.Label */ 38 | .highlight .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */ 39 | .highlight .nt { color: #330099; font-weight: bold } /* Name.Tag */ 40 | .highlight .nv { color: #003333 } /* Name.Variable */ 41 | .highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ 42 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 43 | .highlight .mf { color: #FF6600 } /* Literal.Number.Float */ 44 | .highlight .mh { color: #FF6600 } /* Literal.Number.Hex */ 45 | .highlight .mi { color: #FF6600 } /* Literal.Number.Integer */ 46 | .highlight .mo { color: #FF6600 } /* Literal.Number.Oct */ 47 | .highlight .sb { color: #CC3300 } /* Literal.String.Backtick */ 48 | .highlight .sc { color: #CC3300 } /* Literal.String.Char */ 49 | .highlight .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */ 50 | .highlight .s2 { color: #CC3300 } /* Literal.String.Double */ 51 | .highlight .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */ 52 | .highlight .sh { color: #CC3300 } /* Literal.String.Heredoc */ 53 | .highlight .si { color: #AA0000 } /* Literal.String.Interpol */ 54 | .highlight .sx { color: #CC3300 } /* Literal.String.Other */ 55 | .highlight .sr { color: #33AAAA } /* Literal.String.Regex */ 56 | .highlight .s1 { color: #CC3300 } /* Literal.String.Single */ 57 | .highlight .ss { color: #FFCC33 } /* Literal.String.Symbol */ 58 | .highlight .bp { color: #336666 } /* Name.Builtin.Pseudo */ 59 | .highlight .vc { color: #003333 } /* Name.Variable.Class */ 60 | .highlight .vg { color: #003333 } /* Name.Variable.Global */ 61 | .highlight .vi { color: #003333 } /* Name.Variable.Instance */ 62 | .highlight .il { color: #FF6600 } /* Literal.Number.Integer.Long */ 63 | 64 | .type-csharp .highlight .k { color: #0000FF } 65 | .type-csharp .highlight .kt { color: #0000FF } 66 | .type-csharp .highlight .nf { color: #000000; font-weight: normal } 67 | .type-csharp .highlight .nc { color: #2B91AF } 68 | .type-csharp .highlight .nn { color: #000000 } 69 | .type-csharp .highlight .s { color: #A31515 } 70 | .type-csharp .highlight .sc { color: #A31515 } 71 | -------------------------------------------------------------------------------- /docs/stylesheets/stylesheet.css: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Slate Theme for GitHub Pages 3 | by Jason Costello, @jsncostello 4 | *******************************************************************************/ 5 | 6 | @import url(pygment_trac.css); 7 | 8 | /******************************************************************************* 9 | MeyerWeb Reset 10 | *******************************************************************************/ 11 | 12 | html, body, div, span, applet, object, iframe, 13 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 14 | a, abbr, acronym, address, big, cite, code, 15 | del, dfn, em, img, ins, kbd, q, s, samp, 16 | small, strike, strong, sub, sup, tt, var, 17 | b, u, i, center, 18 | dl, dt, dd, ol, ul, li, 19 | fieldset, form, label, legend, 20 | table, caption, tbody, tfoot, thead, tr, th, td, 21 | article, aside, canvas, details, embed, 22 | figure, figcaption, footer, header, hgroup, 23 | menu, nav, output, ruby, section, summary, 24 | time, mark, audio, video { 25 | margin: 0; 26 | padding: 0; 27 | border: 0; 28 | font: inherit; 29 | vertical-align: baseline; 30 | } 31 | 32 | /* HTML5 display-role reset for older browsers */ 33 | article, aside, details, figcaption, figure, 34 | footer, header, hgroup, menu, nav, section { 35 | display: block; 36 | } 37 | 38 | ol, ul { 39 | list-style: none; 40 | } 41 | 42 | table { 43 | border-collapse: collapse; 44 | border-spacing: 0; 45 | } 46 | 47 | /******************************************************************************* 48 | Theme Styles 49 | *******************************************************************************/ 50 | 51 | body { 52 | box-sizing: border-box; 53 | color:#373737; 54 | background: #212121; 55 | font-size: 16px; 56 | font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif; 57 | line-height: 1.5; 58 | -webkit-font-smoothing: antialiased; 59 | } 60 | 61 | h1, h2, h3, h4, h5, h6 { 62 | margin: 10px 0; 63 | font-weight: 700; 64 | color:#222222; 65 | font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif; 66 | letter-spacing: -1px; 67 | } 68 | 69 | h1 { 70 | font-size: 36px; 71 | font-weight: 700; 72 | } 73 | 74 | h2 { 75 | padding-bottom: 10px; 76 | font-size: 32px; 77 | background: url('../images/bg_hr.png') repeat-x bottom; 78 | } 79 | 80 | h3 { 81 | font-size: 24px; 82 | } 83 | 84 | h4 { 85 | font-size: 21px; 86 | } 87 | 88 | h5 { 89 | font-size: 18px; 90 | } 91 | 92 | h6 { 93 | font-size: 16px; 94 | } 95 | 96 | p { 97 | margin: 10px 0 15px 0; 98 | } 99 | 100 | footer p { 101 | color: #f2f2f2; 102 | } 103 | 104 | a { 105 | text-decoration: none; 106 | color: #007edf; 107 | text-shadow: none; 108 | 109 | transition: color 0.5s ease; 110 | transition: text-shadow 0.5s ease; 111 | -webkit-transition: color 0.5s ease; 112 | -webkit-transition: text-shadow 0.5s ease; 113 | -moz-transition: color 0.5s ease; 114 | -moz-transition: text-shadow 0.5s ease; 115 | -o-transition: color 0.5s ease; 116 | -o-transition: text-shadow 0.5s ease; 117 | -ms-transition: color 0.5s ease; 118 | -ms-transition: text-shadow 0.5s ease; 119 | } 120 | 121 | a:hover, a:focus {text-decoration: underline;} 122 | 123 | footer a { 124 | color: #F2F2F2; 125 | text-decoration: underline; 126 | } 127 | 128 | em { 129 | font-style: italic; 130 | } 131 | 132 | strong { 133 | font-weight: bold; 134 | } 135 | 136 | img { 137 | position: relative; 138 | margin: 0 auto; 139 | max-width: 739px; 140 | padding: 5px; 141 | margin: 10px 0 10px 0; 142 | border: 1px solid #ebebeb; 143 | 144 | box-shadow: 0 0 5px #ebebeb; 145 | -webkit-box-shadow: 0 0 5px #ebebeb; 146 | -moz-box-shadow: 0 0 5px #ebebeb; 147 | -o-box-shadow: 0 0 5px #ebebeb; 148 | -ms-box-shadow: 0 0 5px #ebebeb; 149 | } 150 | 151 | p img { 152 | display: inline; 153 | margin: 0; 154 | padding: 0; 155 | vertical-align: middle; 156 | text-align: center; 157 | border: none; 158 | } 159 | 160 | pre, code { 161 | width: 100%; 162 | color: #222; 163 | background-color: #fff; 164 | 165 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; 166 | font-size: 14px; 167 | 168 | border-radius: 2px; 169 | -moz-border-radius: 2px; 170 | -webkit-border-radius: 2px; 171 | } 172 | 173 | pre { 174 | width: 100%; 175 | padding: 10px; 176 | box-shadow: 0 0 10px rgba(0,0,0,.1); 177 | overflow: auto; 178 | } 179 | 180 | code { 181 | padding: 3px; 182 | margin: 0 3px; 183 | box-shadow: 0 0 10px rgba(0,0,0,.1); 184 | } 185 | 186 | pre code { 187 | display: block; 188 | box-shadow: none; 189 | } 190 | 191 | blockquote { 192 | color: #666; 193 | margin-bottom: 20px; 194 | padding: 0 0 0 20px; 195 | border-left: 3px solid #bbb; 196 | } 197 | 198 | 199 | ul, ol, dl { 200 | margin-bottom: 15px 201 | } 202 | 203 | ul { 204 | list-style: inside; 205 | padding-left: 20px; 206 | } 207 | 208 | ol { 209 | list-style: decimal inside; 210 | padding-left: 20px; 211 | } 212 | 213 | dl dt { 214 | font-weight: bold; 215 | } 216 | 217 | dl dd { 218 | padding-left: 20px; 219 | font-style: italic; 220 | } 221 | 222 | dl p { 223 | padding-left: 20px; 224 | font-style: italic; 225 | } 226 | 227 | hr { 228 | height: 1px; 229 | margin-bottom: 5px; 230 | border: none; 231 | background: url('../images/bg_hr.png') repeat-x center; 232 | } 233 | 234 | table { 235 | border: 1px solid #373737; 236 | margin-bottom: 20px; 237 | text-align: left; 238 | } 239 | 240 | th { 241 | font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif; 242 | padding: 10px; 243 | background: #373737; 244 | color: #fff; 245 | } 246 | 247 | td { 248 | padding: 10px; 249 | border: 1px solid #373737; 250 | } 251 | 252 | form { 253 | background: #f2f2f2; 254 | padding: 20px; 255 | } 256 | 257 | /******************************************************************************* 258 | Full-Width Styles 259 | *******************************************************************************/ 260 | 261 | .outer { 262 | width: 100%; 263 | } 264 | 265 | .inner { 266 | position: relative; 267 | max-width: 640px; 268 | padding: 20px 10px; 269 | margin: 0 auto; 270 | } 271 | 272 | #forkme_banner { 273 | display: block; 274 | position: absolute; 275 | top:0; 276 | right: 10px; 277 | z-index: 10; 278 | padding: 10px 50px 10px 10px; 279 | color: #fff; 280 | background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%; 281 | font-weight: 700; 282 | box-shadow: 0 0 10px rgba(0,0,0,.5); 283 | border-bottom-left-radius: 2px; 284 | border-bottom-right-radius: 2px; 285 | } 286 | 287 | #header_wrap { 288 | background: #212121; 289 | background: -moz-linear-gradient(top, #373737, #212121); 290 | background: -webkit-linear-gradient(top, #373737, #212121); 291 | background: -ms-linear-gradient(top, #373737, #212121); 292 | background: -o-linear-gradient(top, #373737, #212121); 293 | background: linear-gradient(top, #373737, #212121); 294 | } 295 | 296 | #header_wrap .inner { 297 | padding: 50px 10px 30px 10px; 298 | } 299 | 300 | #project_title { 301 | margin: 0; 302 | color: #fff; 303 | font-size: 42px; 304 | font-weight: 700; 305 | text-shadow: #111 0px 0px 10px; 306 | } 307 | 308 | #project_tagline { 309 | color: #fff; 310 | font-size: 24px; 311 | font-weight: 300; 312 | background: none; 313 | text-shadow: #111 0px 0px 10px; 314 | } 315 | 316 | #downloads { 317 | position: absolute; 318 | width: 210px; 319 | z-index: 10; 320 | bottom: -40px; 321 | right: 0; 322 | height: 70px; 323 | background: url('../images/icon_download.png') no-repeat 0% 90%; 324 | } 325 | 326 | .zip_download_link { 327 | display: block; 328 | float: right; 329 | width: 90px; 330 | height:70px; 331 | text-indent: -5000px; 332 | overflow: hidden; 333 | background: url(../images/sprite_download.png) no-repeat bottom left; 334 | } 335 | 336 | .tar_download_link { 337 | display: block; 338 | float: right; 339 | width: 90px; 340 | height:70px; 341 | text-indent: -5000px; 342 | overflow: hidden; 343 | background: url(../images/sprite_download.png) no-repeat bottom right; 344 | margin-left: 10px; 345 | } 346 | 347 | .zip_download_link:hover { 348 | background: url(../images/sprite_download.png) no-repeat top left; 349 | } 350 | 351 | .tar_download_link:hover { 352 | background: url(../images/sprite_download.png) no-repeat top right; 353 | } 354 | 355 | #main_content_wrap { 356 | background: #f2f2f2; 357 | border-top: 1px solid #111; 358 | border-bottom: 1px solid #111; 359 | } 360 | 361 | #main_content { 362 | padding-top: 40px; 363 | } 364 | 365 | #footer_wrap { 366 | background: #212121; 367 | } 368 | 369 | 370 | 371 | /******************************************************************************* 372 | Small Device Styles 373 | *******************************************************************************/ 374 | 375 | @media screen and (max-width: 480px) { 376 | body { 377 | font-size:14px; 378 | } 379 | 380 | #downloads { 381 | display: none; 382 | } 383 | 384 | .inner { 385 | min-width: 320px; 386 | max-width: 480px; 387 | } 388 | 389 | #project_title { 390 | font-size: 32px; 391 | } 392 | 393 | h1 { 394 | font-size: 28px; 395 | } 396 | 397 | h2 { 398 | font-size: 24px; 399 | } 400 | 401 | h3 { 402 | font-size: 21px; 403 | } 404 | 405 | h4 { 406 | font-size: 18px; 407 | } 408 | 409 | h5 { 410 | font-size: 14px; 411 | } 412 | 413 | h6 { 414 | font-size: 12px; 415 | } 416 | 417 | code, pre { 418 | min-width: 320px; 419 | max-width: 480px; 420 | font-size: 11px; 421 | } 422 | 423 | } 424 | -------------------------------------------------------------------------------- /img/screenshot-annotated-2.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/AutoRunCustomTool/4cb0123bf9f4b2f39b033861d0413f50290c88ff/img/screenshot-annotated-2.0.png -------------------------------------------------------------------------------- /img/screenshot-annotated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/AutoRunCustomTool/4cb0123bf9f4b2f39b033861d0413f50290c88ff/img/screenshot-annotated.png -------------------------------------------------------------------------------- /samples/ClassicProject/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /samples/ClassicProject/ClassicProject.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7AAF3CF7-BD0C-4EE8-B9B5-D249B3A098E2} 8 | Exe 9 | ClassicProject 10 | ClassicProject 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | TextTemplatingFileGenerator 57 | target2.txt 58 | 59 | 60 | target2.tt 61 | True 62 | True 63 | Always 64 | 65 | 66 | TextTemplatingFileGenerator 67 | target.txt 68 | 69 | 70 | True 71 | True 72 | target.tt 73 | Always 74 | 75 | 76 | target.tt%3btarget2.tt 77 | Always 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /samples/ClassicProject/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace ClassicProject 5 | { 6 | internal class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | PrintFileContent("trigger.txt"); 11 | PrintFileContent("target.txt"); 12 | PrintFileContent("target2.txt"); 13 | } 14 | 15 | static void PrintFileContent(string fileName) 16 | { 17 | var content = File.ReadAllText(fileName); 18 | Console.WriteLine($"Content of '{fileName}':"); 19 | Console.WriteLine(content); 20 | Console.WriteLine(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/ClassicProject/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ClassicProject")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ClassicProject")] 13 | [assembly: AssemblyCopyright("Copyright © 2022")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7aaf3cf7-bd0c-4ee8-b9b5-d249b3a098e2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /samples/ClassicProject/README.md: -------------------------------------------------------------------------------- 1 | # Sample: ClassicProject 2 | 3 | This project contains: 4 | 5 | - A "trigger" file: `trigger.txt` 6 | - Two "target" T4 templates: `target.tt` and `target2.tt` 7 | 8 | The T4 templates use the content of `trigger.txt` to generate two other text files (`target.txt` and `target2.txt`). 9 | 10 | The trigger file is configured so that any change to this file causes the T4 templates to regenerate their output. In "classic" projects (pre .NET Core format), 11 | this configuration is done via the Properties UI by setting the "Run custom tool on" property: 12 | 13 | ![Properties](properties.png) 14 | -------------------------------------------------------------------------------- /samples/ClassicProject/properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomaslevesque/AutoRunCustomTool/4cb0123bf9f4b2f39b033861d0413f50290c88ff/samples/ClassicProject/properties.png -------------------------------------------------------------------------------- /samples/ClassicProject/target.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="true" language="C#" #> 2 | <#@ output extension=".txt" #> 3 | <# 4 | string dir = System.IO.Path.GetDirectoryName(Host.TemplateFile); 5 | string triggerPath = System.IO.Path.Combine(dir, "trigger.txt"); 6 | string content = System.IO.File.ReadAllText(triggerPath); 7 | #> 8 | This file was last generated <#= DateTime.Now #> by template 'target.tt' 9 | The content of the trigger file is: <#= content #> -------------------------------------------------------------------------------- /samples/ClassicProject/target.txt: -------------------------------------------------------------------------------- 1 | This file was last generated 03/09/2022 10:02:04 by template 'target.tt' 2 | The content of the trigger file is: hello world! -------------------------------------------------------------------------------- /samples/ClassicProject/target2.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="true" language="C#" #> 2 | <#@ output extension=".txt" #> 3 | <# 4 | string dir = System.IO.Path.GetDirectoryName(Host.TemplateFile); 5 | string triggerPath = System.IO.Path.Combine(dir, "trigger.txt"); 6 | string content = System.IO.File.ReadAllText(triggerPath); 7 | #> 8 | This file was last generated <#= DateTime.Now #> by template 'target2.tt' 9 | The content of the trigger file is: <#= content #> -------------------------------------------------------------------------------- /samples/ClassicProject/target2.txt: -------------------------------------------------------------------------------- 1 | This file was last generated 03/09/2022 10:02:04 by template 'target2.tt' 2 | The content of the trigger file is: hello world! -------------------------------------------------------------------------------- /samples/ClassicProject/trigger.txt: -------------------------------------------------------------------------------- 1 | hello world! -------------------------------------------------------------------------------- /samples/SdkStyleProject/Program.cs: -------------------------------------------------------------------------------- 1 | PrintFileContent("trigger.txt"); 2 | PrintFileContent("target.txt"); 3 | PrintFileContent("target2.txt"); 4 | 5 | static void PrintFileContent(string fileName) 6 | { 7 | var content = File.ReadAllText(fileName); 8 | Console.WriteLine($"Content of '{fileName}':"); 9 | Console.WriteLine(content); 10 | Console.WriteLine(); 11 | } 12 | -------------------------------------------------------------------------------- /samples/SdkStyleProject/README.md: -------------------------------------------------------------------------------- 1 | # Sample: SdkStyleProject 2 | 3 | This project contains: 4 | 5 | - A "trigger" file: `trigger.txt` 6 | - Two "target" T4 templates: `target.tt` and `target2.tt` 7 | 8 | The T4 templates use the content of `trigger.txt` to generate two other text files (`target.txt` and `target2.txt`). 9 | 10 | The trigger file is configured so that any change to this file causes the T4 templates to regenerate their output. In "SDK-style" projects 11 | (.NET Core / .NET Standard / .NET 5.0+ format), this configuration is done directly in the project file: 12 | 13 | ```xml 14 | 15 | target.tt;target2.tt 16 | 17 | ``` -------------------------------------------------------------------------------- /samples/SdkStyleProject/SdkStyleProject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | target.txt 21 | TextTemplatingFileGenerator 22 | 23 | 24 | Always 25 | True 26 | True 27 | target.tt 28 | 29 | 30 | target2.txt 31 | TextTemplatingFileGenerator 32 | 33 | 34 | Always 35 | True 36 | True 37 | target2.tt 38 | 39 | 40 | Always 41 | target.tt;target2.tt 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /samples/SdkStyleProject/target.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="true" language="C#" #> 2 | <#@ output extension=".txt" #> 3 | <# 4 | string dir = System.IO.Path.GetDirectoryName(Host.TemplateFile); 5 | string triggerPath = System.IO.Path.Combine(dir, "trigger.txt"); 6 | string content = System.IO.File.ReadAllText(triggerPath); 7 | #> 8 | This file was last generated <#= DateTime.Now #> by template 'target.tt' 9 | The content of the trigger file is: <#= content #> -------------------------------------------------------------------------------- /samples/SdkStyleProject/target.txt: -------------------------------------------------------------------------------- 1 | This file was last generated 03/09/2022 10:11:53 by template 'target.tt' 2 | The content of the trigger file is: hello world!!! -------------------------------------------------------------------------------- /samples/SdkStyleProject/target2.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="true" language="C#" #> 2 | <#@ output extension=".txt" #> 3 | <# 4 | string dir = System.IO.Path.GetDirectoryName(Host.TemplateFile); 5 | string triggerPath = System.IO.Path.Combine(dir, "trigger.txt"); 6 | string content = System.IO.File.ReadAllText(triggerPath); 7 | #> 8 | This file was last generated <#= DateTime.Now #> by template 'target2.tt' 9 | The content of the trigger file is: <#= content #> -------------------------------------------------------------------------------- /samples/SdkStyleProject/target2.txt: -------------------------------------------------------------------------------- 1 | This file was last generated 03/09/2022 10:11:53 by template 'target2.tt' 2 | The content of the trigger file is: hello world!!! -------------------------------------------------------------------------------- /samples/SdkStyleProject/trigger.txt: -------------------------------------------------------------------------------- 1 | hello world!!! --------------------------------------------------------------------------------