From f3e609a3e6c97c1259b07e09fb54311ba083a190 Mon Sep 17 00:00:00 2001 From: Chris Savoie Date: Thu, 21 Jun 2018 17:56:54 -0700 Subject: [PATCH 1/5] [Csproj] Add a field for custom properties that are added after imports do be able to reference default msbuild functionality. * This was needed to be able to extend the "BuildDependsOn" property to add custom targets. --- Sharpmake.Generators/VisualStudio/Csproj.cs | 7 ++++--- Sharpmake/Project.cs | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Sharpmake.Generators/VisualStudio/Csproj.cs b/Sharpmake.Generators/VisualStudio/Csproj.cs index 660d2ee22..fe9118310 100644 --- a/Sharpmake.Generators/VisualStudio/Csproj.cs +++ b/Sharpmake.Generators/VisualStudio/Csproj.cs @@ -1179,7 +1179,7 @@ List skipFiles var preImportCustomProperties = new Dictionary(project.PreImportCustomProperties); AddPreImportCustomProperties(preImportCustomProperties, project, projectPath); - WriteCustomProperties(preImportCustomProperties, project, writer, resolver); + WriteCustomProperties(preImportCustomProperties, writer, resolver); var preImportProjects = new List(project.PreImportProjects); WriteImportProjects(preImportProjects.Distinct(EqualityComparer.Default), project, configurations.First(), writer, resolver); @@ -1281,7 +1281,7 @@ List skipFiles Write(Template.MSBuild14PropertyGroup, writer, resolver); } - WriteCustomProperties(project.CustomProperties, project, writer, resolver); + WriteCustomProperties(project.CustomProperties, writer, resolver); if (project.ProjectTypeGuids == CSharpProjectType.Wcf) { @@ -1501,6 +1501,7 @@ List skipFiles } WriteImportProjects(importProjects.Distinct(EqualityComparer.Default), project, configurations.First(), writer, resolver); + WriteCustomProperties(project.PostImportCustomProperties, writer, resolver); foreach (var element in project.UsingTasks) { @@ -1614,7 +1615,7 @@ private static void WriteImportProjects(IEnumerable importProject } // TODO: remove this and use Sharpmake.Generators.VisualStudio.VsProjCommon.WriteCustomProperties instead - private static void WriteCustomProperties(Dictionary customProperties, Project project, StreamWriter writer, Resolver resolver) + private static void WriteCustomProperties(Dictionary customProperties, StreamWriter writer, Resolver resolver) { if (customProperties.Any()) { diff --git a/Sharpmake/Project.cs b/Sharpmake/Project.cs index a4ca9d2be..8604ee2d2 100644 --- a/Sharpmake/Project.cs +++ b/Sharpmake/Project.cs @@ -233,6 +233,7 @@ internal void Resolve(string sourceRootPath, Resolver resolver) public Dictionary PreImportCustomProperties = new Dictionary(); // pre import properties are added before any imports to the project xml as Value public Dictionary CustomProperties = new Dictionary(); // custom properties are added to the project xml as Value + public Dictionary PostImportCustomProperties = new Dictionary(); // additional custom properties that are added after imports public Dictionary CustomFilterMapping = new Dictionary(); /// maps relative source directory to a custom filter path for vcxproj.filter files From 8bf55d477484db0f2f152c9b6ae2e65a2b4e4363 Mon Sep 17 00:00:00 2001 From: Chris Savoie Date: Wed, 9 Feb 2022 21:23:53 -0800 Subject: [PATCH 2/5] [Csproj] Add properties to be able to set interop references by name, path and name external. --- Sharpmake.Generators/VisualStudio/Csproj.cs | 32 +++++++++++++++++++++ Sharpmake/Project.Configuration.cs | 3 ++ 2 files changed, 35 insertions(+) diff --git a/Sharpmake.Generators/VisualStudio/Csproj.cs b/Sharpmake.Generators/VisualStudio/Csproj.cs index fe9118310..c5bef9326 100644 --- a/Sharpmake.Generators/VisualStudio/Csproj.cs +++ b/Sharpmake.Generators/VisualStudio/Csproj.cs @@ -2286,6 +2286,16 @@ List skipFiles }; itemGroups.AddReference(dotNetFramework, referencesByName); } + foreach (var str in conf.InteropReferencesByName) + { + var referencesByName = new ItemGroups.Reference + { + Include = str, + Private = project.DependenciesCopyLocal.HasFlag(Project.DependenciesCopyLocalTypes.DotNetReferences) ? default(bool?) : false, + EmbedInteropTypes = true + }; + itemGroups.AddReference(dotNetFramework, referencesByName); + } } } @@ -2301,6 +2311,16 @@ List skipFiles }; itemGroups.AddReference(dotNetFramework, referencesByNameExternal); } + foreach (var str in conf.InteropReferencesByNameExternal) + { + var referencesByNameExternal = new ItemGroups.Reference + { + Include = str, + Private = project.DependenciesCopyLocal.HasFlag(Project.DependenciesCopyLocalTypes.DotNetExtensions), + EmbedInteropTypes = true + }; + itemGroups.AddReference(dotNetFramework, referencesByNameExternal); + } } foreach (var conf in configurations) @@ -2317,6 +2337,18 @@ List skipFiles }; itemGroups.AddReference(dotNetFramework, referencesByPath); } + foreach (var str in conf.InteropReferencesByPath.Select(Util.GetCapitalizedPath)) + { + var referencesByPath = new ItemGroups.Reference + { + Include = Path.GetFileNameWithoutExtension(str), + SpecificVersion = false, + HintPath = Util.PathGetRelative(_projectPathCapitalized, str), + Private = project.DependenciesCopyLocal.HasFlag(Project.DependenciesCopyLocalTypes.ExternalReferences), + EmbedInteropTypes = true + }; + itemGroups.AddReference(dotNetFramework, referencesByPath); + } foreach (var str in project.AdditionalEmbeddedAssemblies.Select(Util.GetCapitalizedPath)) { diff --git a/Sharpmake/Project.Configuration.cs b/Sharpmake/Project.Configuration.cs index e5e68dd89..ebd40fa05 100644 --- a/Sharpmake/Project.Configuration.cs +++ b/Sharpmake/Project.Configuration.cs @@ -2819,6 +2819,9 @@ internal void Resolve(string sourceRootPath, Resolver resolver) public Strings ReferencesByName = new Strings(); public Strings ReferencesByNameExternal = new Strings(); public Strings ReferencesByPath = new Strings(); + public Strings InteropReferencesByName = new Strings(); + public Strings InteropReferencesByNameExternal = new Strings(); + public Strings InteropReferencesByPath = new Strings(); public string ConditionalReferencesByPathCondition = string.Empty; public Strings ConditionalReferencesByPath = new Strings(); public Strings ForceUsingFiles = new Strings(); From f3e7be5b4aadd1cc727d76c73e98311a3782262b Mon Sep 17 00:00:00 2001 From: Chris Savoie Date: Thu, 21 Jun 2018 15:31:05 -0700 Subject: [PATCH 3/5] [Csproj] Add ability to add non-embedded resources to a csharp project. --- Sharpmake.Generators/VisualStudio/Csproj.cs | 2 +- Sharpmake/DebugProjectGenerator.cs | 1 + Sharpmake/Project.cs | 11 ++++++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Sharpmake.Generators/VisualStudio/Csproj.cs b/Sharpmake.Generators/VisualStudio/Csproj.cs index c5bef9326..adf24d65e 100644 --- a/Sharpmake.Generators/VisualStudio/Csproj.cs +++ b/Sharpmake.Generators/VisualStudio/Csproj.cs @@ -1742,7 +1742,7 @@ List skipFiles HashSet allContents = new HashSet(itemGroups.Contents.Select(c => c.Include)); List resolvedSources = project.ResolvedSourceFiles.Select(source => Util.PathGetRelative(_projectPathCapitalized, Project.GetCapitalizedFile(source))).ToList(); - List resolvedResources = project.ResourceFiles.Concat(project.ResolvedResourcesFullFileNames).Select(resource => Util.PathGetRelative(_projectPathCapitalized, Project.GetCapitalizedFile(resource))).Distinct().ToList(); + List resolvedResources = project.ResourceFiles.Concat(project.NonEmbeddedResourceFiles).Concat(project.ResolvedResourcesFullFileNames).Select(resource => Util.PathGetRelative(_projectPathCapitalized, Project.GetCapitalizedFile(resource))).Distinct().ToList(); List resolvedEmbeddedResource = project.ResourceFiles.Concat(project.AdditionalEmbeddedResource).Concat(project.AdditionalEmbeddedAssemblies).Select(f => Util.PathGetRelative(_projectPathCapitalized, Project.GetCapitalizedFile(f))).Distinct().ToList(); List resolvedNoneFiles = (project.NoneFiles.Select(file => Util.PathGetRelative(_projectPathCapitalized, Project.GetCapitalizedFile(file)))) diff --git a/Sharpmake/DebugProjectGenerator.cs b/Sharpmake/DebugProjectGenerator.cs index f0fa06684..28f0536ae 100644 --- a/Sharpmake/DebugProjectGenerator.cs +++ b/Sharpmake/DebugProjectGenerator.cs @@ -322,6 +322,7 @@ public DebugProject() // ensure that no file will be automagically added SourceFilesExtensions.Clear(); ResourceFilesExtensions.Clear(); + NonEmbeddedResourceFilesExtensions.Clear(); PRIFilesExtensions.Clear(); ResourceFiles.Clear(); NoneExtensions.Clear(); diff --git a/Sharpmake/Project.cs b/Sharpmake/Project.cs index 8604ee2d2..9d678780a 100644 --- a/Sharpmake/Project.cs +++ b/Sharpmake/Project.cs @@ -155,6 +155,9 @@ public int SourceFilesFiltersCount public Strings ResourceFiles = new Strings(); public Strings ResourceFilesExtensions = new Strings(); + public Strings NonEmbeddedResourceFiles = new Strings(); + public Strings NonEmbeddedResourceFilesExtensions = new Strings(); + public Strings NatvisFiles = new Strings(); public Strings NatvisFilesExtensions = new Strings(".natvis"); @@ -864,7 +867,7 @@ internal virtual void ResolveSourceFiles(Builder builder) } // Only scan directory for files if needed - if (SourceFilesExtensions.Count != 0 || ResourceFilesExtensions.Count != 0 || PRIFilesExtensions.Count != 0 || NoneExtensions.Count != 0 || NoneExtensionsCopyIfNewer.Count != 0) + if (SourceFilesExtensions.Count != 0 || ResourceFilesExtensions.Count != 0 || NonEmbeddedResourceFilesExtensions.Count != 0 || PRIFilesExtensions.Count != 0 || NoneExtensions.Count != 0 || NoneExtensionsCopyIfNewer.Count != 0) { string capitalizedSourceRootPath = Util.GetCapitalizedPath(SourceRootPath); @@ -896,6 +899,7 @@ internal virtual void ResolveSourceFiles(Builder builder) AddMatchExtensionFiles(additionalFiles, ref PRIFiles, PRIFilesExtensions); AddMatchExtensionFiles(additionalFiles, ref ResourceFiles, ResourceFilesExtensions); + AddMatchExtensionFiles(additionalFiles, ref NonEmbeddedResourceFiles, NonEmbeddedResourceFilesExtensions); AddMatchExtensionFiles(additionalFiles, ref NatvisFiles, NatvisFilesExtensions); AddMatchExtensionFiles(additionalFiles, ref NoneFiles, NoneExtensions); AddMatchExtensionFiles(additionalFiles, ref NoneFilesCopyIfNewer, NoneExtensionsCopyIfNewer); @@ -924,6 +928,9 @@ internal virtual void ResolveSourceFiles(Builder builder) AddMatchExtensionFiles(files, ref ResourceFiles, ResourceFilesExtensions); Util.ResolvePath(SourceRootPath, ref ResourceFiles); + AddMatchExtensionFiles(files, ref NonEmbeddedResourceFiles, NonEmbeddedResourceFilesExtensions); + Util.ResolvePath(SourceRootPath, ref NonEmbeddedResourceFiles); + AddMatchExtensionFiles(files, ref NatvisFiles, NatvisFilesExtensions); Util.ResolvePath(SourceRootPath, ref NatvisFiles); @@ -1966,6 +1973,7 @@ public FastBuildAllProject(Type targetType) // Disable automatic source files discovery SourceFilesExtensions.Clear(); ResourceFilesExtensions.Clear(); + NonEmbeddedResourceFilesExtensions.Clear(); PRIFilesExtensions.Clear(); } } @@ -2157,6 +2165,7 @@ public static void InitAspNetProject(this CSharpProject aspNetProject) aspNetProject.ContentExtension.Add(contentExtension); aspNetProject.ResourceFilesExtensions.Remove(contentExtension); + aspNetProject.NonEmbeddedResourceFilesExtensions.Remove(contentExtension); aspNetProject.EmbeddedResourceExtensions.Remove(contentExtension); aspNetProject.NoneExtensions.Add(".pubxml"); From 3c3676209022dd290131edb14fc4853901ef597b Mon Sep 17 00:00:00 2001 From: Chris Savoie Date: Thu, 21 Jun 2018 15:41:41 -0700 Subject: [PATCH 4/5] [Csproj] Add support for texttemplates that use Host.SetFileExtension instead of output extension tags. * Add dictionary helper GetOrAdd that will only create the new items on demand instead of having to create them at the function call site --- Sharpmake.Generators/VisualStudio/Csproj.cs | 2 +- Sharpmake/Util.cs | 63 ++++++++++++++++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/Sharpmake.Generators/VisualStudio/Csproj.cs b/Sharpmake.Generators/VisualStudio/Csproj.cs index adf24d65e..8af4f06a2 100644 --- a/Sharpmake.Generators/VisualStudio/Csproj.cs +++ b/Sharpmake.Generators/VisualStudio/Csproj.cs @@ -2170,7 +2170,7 @@ List skipFiles bool runtimeTemplate = project.AdditionalRuntimeTemplates.Contains(ttFile); string expectedExtension = runtimeTemplate ? ".cs" : - Util.GetTextTemplateDirectiveParam(Path.Combine(_projectPath, ttFile), "output", "extension") ?? ".cs"; + Util.GetTextTemplateOutputExtension(Path.Combine(_projectPath, ttFile)) ?? ".cs"; if (!expectedExtension.StartsWith(".", StringComparison.Ordinal)) expectedExtension = "." + expectedExtension; string fileNameWithoutExtension = ttFile.Substring(0, ttFile.Length - TTExtension.Length); diff --git a/Sharpmake/Util.cs b/Sharpmake/Util.cs index 23f55a1e2..1dc7746ab 100644 --- a/Sharpmake/Util.cs +++ b/Sharpmake/Util.cs @@ -98,6 +98,24 @@ public static int GetDeterministicHashCode(this string str) } } + static readonly Dictionary _ttDirectiveRegexes = new Dictionary(); + private static string GetTextTemplateDirectiveParam(string[] templateText, string directive, string paramName) + { + Regex regex = _ttDirectiveRegexes.GetValueOrAdd($"{directive}+{paramName}", () => + new Regex(@"<#@\s*?" + directive + @"\s+?.*?" + paramName + @"=""(?.*?)"".*?#>", RegexOptions.Compiled) + ); + + foreach (var line in templateText) + { + Match m = regex.Match(line); + Group g = m.Groups["paramValue"]; + if (g != null && g.Success) + return g.Value; + } + + return null; + } + /// /// Finds the first occurrence of directive and returns the /// requested param value. Ex: @@ -107,14 +125,33 @@ public static int GetDeterministicHashCode(this string str) /// and return ".txt" /// public static string GetTextTemplateDirectiveParam(string filePath, string directive, string paramName) + { + return GetTextTemplateDirectiveParam(File.ReadAllLines(filePath), directive, paramName); + } + + /// + /// Finds the output type of a template, looking for both the directive form and the Host.SetFileExtension form + /// will match: + /// <#@ output extension=".txt" #> + /// or + /// Host.SetFileExtension(".txt") + /// and return ".txt" + /// + /// + static readonly Regex _ttFileExtensionRegex = new Regex(@"Host.SetFileExtension\(""(?.*?)""\)", RegexOptions.Compiled); + public static string GetTextTemplateOutputExtension(string filePath) { string[] templateText = File.ReadAllLines(filePath); - Regex regex = new Regex(@"<#@\s*?" + directive + @"\s+?.*?" + paramName + @"=""(?.*?)"".*?#>"); + var output = Util.GetTextTemplateDirectiveParam(templateText, "output", "extension"); + if (output != null) + return output; + + // alternatively look for host.SetFileExtension foreach (var line in templateText) { - Match m = regex.Match(line); + Match m = _ttFileExtensionRegex.Match(line); Group g = m.Groups["paramValue"]; if (g != null && g.Success) return g.Value; @@ -1481,6 +1518,28 @@ public static Value GetValueOrAdd(this IDictionary dicti return addValue; } + /// + /// Extension GetValueOrAdd gets the value at the given key or adds at the given key the value provided by the func argument + /// + /// Type of the key + /// Type of the value + /// dictionary in which to search + /// key of the value + /// functon to create the new value + /// the value at the given key (created or not in this call) + public static Value GetValueOrAdd(this IDictionary dictionary, Key key, Func addValueFunc) + { + Value value; + if (dictionary.TryGetValue(key, out value) == false) + { + value = addValueFunc(); + dictionary.Add(key, value); + } + + return value; + } + + public static Value AddOrUpdateValue(this IDictionary dictionary, Key key, Value newValue, Func update) { Value currentValue; From 853340c0074bc8bd97d479ef22e0ca2468d14601 Mon Sep 17 00:00:00 2001 From: Chris Savoie Date: Thu, 21 Jun 2018 15:44:37 -0700 Subject: [PATCH 5/5] [Csproj] Add the ability for a project to specify its default copy behavior for content items. --- Sharpmake.Generators/VisualStudio/Csproj.cs | 9 +-------- Sharpmake/Project.cs | 8 ++++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Sharpmake.Generators/VisualStudio/Csproj.cs b/Sharpmake.Generators/VisualStudio/Csproj.cs index 8af4f06a2..9e1e5f225 100644 --- a/Sharpmake.Generators/VisualStudio/Csproj.cs +++ b/Sharpmake.Generators/VisualStudio/Csproj.cs @@ -1630,13 +1630,6 @@ private static void WriteCustomProperties(Dictionary customPrope } } - internal enum CopyToOutputDirectory - { - Never, - Always, - PreserveNewest - } - private void GenerateFiles( CSharpProject project, List configurations, @@ -1650,7 +1643,7 @@ List skipFiles foreach (var file in project.ResolvedContentFullFileNames) { string include = Util.PathGetRelative(_projectPathCapitalized, file); - itemGroups.Contents.Add(new ItemGroups.Content { Include = include, LinkFolder = project.GetLinkFolder(include) }); + itemGroups.Contents.Add(new ItemGroups.Content { Include = include, CopyToOutputDirectory = project.DefaultContentCopyOperation, LinkFolder = project.GetLinkFolder(include) }); } diff --git a/Sharpmake/Project.cs b/Sharpmake/Project.cs index 9d678780a..e0c5ee2e9 100644 --- a/Sharpmake/Project.cs +++ b/Sharpmake/Project.cs @@ -2060,6 +2060,13 @@ public enum FileType Assembly, File } + + public enum CopyToOutputDirectory + { + Never, + Always, + PreserveNewest + } public class PublishFile { @@ -2258,6 +2265,7 @@ public void AddDefaultReferences(Configuration conf) public class CSharpProject : Project { public Strings ContentExtension = new Strings(); + public CopyToOutputDirectory? DefaultContentCopyOperation = null; public Strings VsctExtension = new Strings(".vsct"); public CSharpProjectType ProjectTypeGuids = CSharpProjectType.Default; public CSharpProjectSchema ProjectSchema = CSharpProjectSchema.Default;