diff --git a/src/Mobile.BuildTools/Configuration/MetaData.cs b/src/Mobile.BuildTools/Configuration/MetaData.cs new file mode 100644 index 00000000..e9a4417b --- /dev/null +++ b/src/Mobile.BuildTools/Configuration/MetaData.cs @@ -0,0 +1,8 @@ +namespace Mobile.BuildTools.Configuration +{ + internal static class MetaData + { + public const string Link = nameof(Link); + public const string SourceFile = nameof(SourceFile); + } +} diff --git a/src/Mobile.BuildTools/ConfigurationManager.targets b/src/Mobile.BuildTools/ConfigurationManager.targets index a36f35df..3457dc32 100644 --- a/src/Mobile.BuildTools/ConfigurationManager.targets +++ b/src/Mobile.BuildTools/ConfigurationManager.targets @@ -18,21 +18,13 @@ IntermediateOutputPath="$(IntermediateOutputPath)" TargetFrameworkIdentifier="$(TargetFrameworkIdentifier)" InputConfigFiles="@(MobileBuildToolsConfig)"> - - - + AfterTargets="_CollectAppConfigs"> $(Configuration) @@ -43,12 +35,14 @@ IntermediateOutputPath="$(IntermediateOutputPath)" TargetFrameworkIdentifier="$(TargetFrameworkIdentifier)" AppConfigEnvironment="$(AppConfigEnvironment)" - InputConfigFiles="@(BuildToolsTempConfigs)" - ExpectedAppConfig="@(GeneratedAppConfig)"> + InputConfigFiles="@(MobileBuildToolsConfig)" + GeneratedAppConfig="@(GeneratedAppConfig)"> + TaskParameter="OutputConfigs"/> + TaskParameter="OutputConfigs"/> + diff --git a/src/Mobile.BuildTools/Generators/AppConfig/ConfigurationManagerTransformationGenerator.cs b/src/Mobile.BuildTools/Generators/AppConfig/ConfigurationManagerTransformationGenerator.cs index 94b3c774..63cdbbc3 100644 --- a/src/Mobile.BuildTools/Generators/AppConfig/ConfigurationManagerTransformationGenerator.cs +++ b/src/Mobile.BuildTools/Generators/AppConfig/ConfigurationManagerTransformationGenerator.cs @@ -1,22 +1,15 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -using System.Xml; -using System.Xml.Linq; -using System.Xml.Xsl; -using Microsoft.Build.Evaluation; using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; using Mobile.BuildTools.Build; using Mobile.BuildTools.Configuration; using Mobile.BuildTools.Generators; -using Mobile.BuildTools.Logging; - +using Mobile.BuildTools.Models.Configuration; + namespace Mobile.BuildTools.Tasks.Generators.AppConfig { - internal class ConfigurationManagerTransformationGenerator : GeneratorBase> + internal class ConfigurationManagerTransformationGenerator : GeneratorBase { public ConfigurationManagerTransformationGenerator(IBuildConfiguration buildConfiguration) : base(buildConfiguration) @@ -29,53 +22,32 @@ public ConfigurationManagerTransformationGenerator(IBuildConfiguration buildConf // C:/repos/MyProject/MyProject.iOS/app.config public string TransformFilePath { get; set; } + public IEnumerable ExpectedConfigs { get; set; } + protected override void ExecuteInternal() { - var parentDirectory = Directory.GetParent(BaseConfigPath); - var configFileName = Path.GetFileNameWithoutExtension(BaseConfigPath); - + // Copy all output files... + foreach (var config in ExpectedConfigs) + { + Log.LogMessage($"Copying '{config.SourceFile}' to '{config.OutputPath}'."); + File.Copy(config.SourceFile, config.OutputPath); + } + if (!string.IsNullOrEmpty(TransformFilePath) && File.Exists(TransformFilePath)) { var updatedConfig = TransformationHelper.Transform(File.ReadAllText(BaseConfigPath), - File.ReadAllText(TransformFilePath)); + File.ReadAllText(TransformFilePath)); if (updatedConfig is null) return; - updatedConfig.Save(BaseConfigPath); + var appConfig = ExpectedConfigs.First(x => Path.GetFileName(x.OutputPath) == "app.config"); + updatedConfig.Save(appConfig.OutputPath); if (Build.Configuration.Debug) { Log.LogMessage("*************** Transformed app.config ******************************"); Log.LogMessage(updatedConfig.ToString()); } - } - - switch(Build.Configuration.AppConfig.Strategy) - { - case Models.AppConfigStrategy.BundleAll: - Outputs = parentDirectory.EnumerateFiles() - .Select(x => new TaskItem(x.FullName)); - Log.LogMessage($"All app.config's will be bundled. {string.Join(", ", parentDirectory.EnumerateFiles().Select(x => x.Name))}"); - break; - case Models.AppConfigStrategy.BundleNonStandard: - var standardConfigs = new[] - { - "app.debug.config", - "app.release.config", - "app.store.config", - "app.adhoc.config" - }; - Outputs = parentDirectory.EnumerateFiles() - .Where(x => !standardConfigs.Any(s => s.Equals(x.Name, StringComparison.InvariantCultureIgnoreCase))) - .Select(x => new TaskItem(x.FullName)); - if(Outputs.Count() > 1) - { - Log.LogMessage($"The app.config will be bundled with the following configurations. {string.Join(", ", Outputs.Select(x => Path.GetFileName(x.ItemSpec)))}"); - } - break; - default: - Outputs = new[] { new TaskItem(BaseConfigPath) }; - break; - } + } } } } diff --git a/src/Mobile.BuildTools/Models/Configuration/ExpectedAppConfig.cs b/src/Mobile.BuildTools/Models/Configuration/ExpectedAppConfig.cs new file mode 100644 index 00000000..8b4dddff --- /dev/null +++ b/src/Mobile.BuildTools/Models/Configuration/ExpectedAppConfig.cs @@ -0,0 +1,17 @@ +namespace Mobile.BuildTools.Models.Configuration +{ + internal class ExpectedAppConfig + { + public string OutputPath { get; set; } + public string SourceFile { get; set; } + +#if !(SCHEMAGENERATOR || CLI_TOOL) + public static ExpectedAppConfig FromTaskItem(Microsoft.Build.Framework.ITaskItem item) => + new ExpectedAppConfig + { + OutputPath = item.ItemSpec, + SourceFile = item.GetMetadata(BuildTools.Configuration.MetaData.SourceFile) + }; +#endif + } +} diff --git a/src/Mobile.BuildTools/MonoAndroid.targets b/src/Mobile.BuildTools/MonoAndroid.targets index 05425c2e..40b6c8f7 100644 --- a/src/Mobile.BuildTools/MonoAndroid.targets +++ b/src/Mobile.BuildTools/MonoAndroid.targets @@ -36,7 +36,7 @@ AfterTargets="_CollectAppConfigs;ConfigurationManagerTransformAndCopy" BeforeTargets="_CreateDex;Compile"> - diff --git a/src/Mobile.BuildTools/Tasks/AppConfigCopyTask.cs b/src/Mobile.BuildTools/Tasks/AppConfigCopyTask.cs index 4f318fa7..54953cc5 100644 --- a/src/Mobile.BuildTools/Tasks/AppConfigCopyTask.cs +++ b/src/Mobile.BuildTools/Tasks/AppConfigCopyTask.cs @@ -2,38 +2,24 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Mobile.BuildTools.Build; +using Mobile.BuildTools.Configuration; namespace Mobile.BuildTools.Tasks { public class AppConfigCopyTask : BuildToolsTaskBase { - private const string EmptyAppConfig = @" - - - - - -"; - public ITaskItem[] InputConfigFiles { get; set; } private List _outputs = new List(); [Output] public ITaskItem[] OutputConfigs => _outputs.ToArray(); - private List _copiedConfigs = new List(); - [Output] - public ITaskItem[] CopiedConfigs => _copiedConfigs.ToArray(); - internal override void ExecuteInternal(IBuildConfiguration config) { - // if (!System.Diagnostics.Debugger.IsAttached) - // System.Diagnostics.Debugger.Launch(); - + // Validate Inputs if (!InputConfigFiles.Any()) { Log.LogMessage("No input config files were found"); @@ -48,47 +34,41 @@ internal override void ExecuteInternal(IBuildConfiguration config) return; } - var files = InputConfigFiles.Select(x => x.ItemSpec); - var configsOutputDir = Path.Combine(IntermediateOutputPath, "configs"); - - if (!Directory.Exists(configsOutputDir)) + // Ensure there is an app.config available + if (!InputConfigFiles.Any(x => FileNameEquals("app.config", x.ItemSpec) || + (!string.IsNullOrEmpty(x.GetMetadata("Link")) && FileNameEquals("app.config", x.GetMetadata("Link"))))) { - Directory.CreateDirectory(configsOutputDir); + Log.LogError("You must have at least one file included with the build action MobileBuildToolsConfig with the file name app.config"); + return; } - foreach (var file in files) - { - CopyFile(configsOutputDir, file, config); - } + var configsOutputDir = Path.Combine(IntermediateOutputPath, "configs"); - if(!_outputs.Any(x => Path.GetFileName(x.ItemSpec).Equals("app.config", StringComparison.InvariantCultureIgnoreCase))) + // Determine which files need outputs + //var files = InputConfigFiles.Select(x => x.ItemSpec); + foreach (var item in InputConfigFiles) { - var projectFile = Path.Combine(config.ProjectDirectory, "app.config"); - File.WriteAllText(projectFile, EmptyAppConfig); - CopyFile(configsOutputDir, projectFile, config); + CollectValidOutput(configsOutputDir, item, config); } } - private void CopyFile(string configsOutputDir, string file, IBuildConfiguration config) - { - var outputFilePath = Path.Combine(configsOutputDir, Path.GetFileName(file)); - var lockFilePath = $"{outputFilePath}.lock"; - var inputFileInfo = new FileInfo(file); + private static bool FileNameEquals(string expected, string actualPath) => + Path.GetFileName(actualPath).Equals(expected, StringComparison.InvariantCultureIgnoreCase); - if (!File.Exists(outputFilePath) || !File.Exists(lockFilePath) || - DateTime.Parse(File.ReadAllText(lockFilePath)) < inputFileInfo.LastWriteTimeUtc) - { - config.Logger.LogMessage($"Copying {file} to {outputFilePath}"); - inputFileInfo.CopyTo(outputFilePath); - File.WriteAllText(lockFilePath, $"{inputFileInfo.LastWriteTimeUtc.ToString()}"); - } - - _copiedConfigs.Add(new TaskItem(outputFilePath)); + private void CollectValidOutput(string configsOutputDir, ITaskItem item, IBuildConfiguration config) + { + var filePath = item.ItemSpec; + var link = item.GetMetadata(MetaData.Link); + var fileName = !string.IsNullOrEmpty(link) ? Path.GetFileName(link) : Path.GetFileName(filePath); + var outputFilePath = Path.Combine(configsOutputDir, fileName); + var outputItem = new TaskItem(outputFilePath); + outputItem.SetMetadata(MetaData.Link, link); + outputItem.SetMetadata(MetaData.SourceFile, filePath); switch (config.Configuration.AppConfig.Strategy) { case Models.AppConfigStrategy.BundleAll: - _outputs.Add(new TaskItem(outputFilePath)); + _outputs.Add(outputItem); break; case Models.AppConfigStrategy.BundleNonStandard: var standardConfigs = new[] @@ -98,15 +78,15 @@ private void CopyFile(string configsOutputDir, string file, IBuildConfiguration "app.store.config", "app.adhoc.config" }; - if(!standardConfigs.Any(x => x.Equals(Path.GetFileName(file), StringComparison.InvariantCultureIgnoreCase))) + if(!standardConfigs.Any(x => x.Equals(fileName, StringComparison.InvariantCultureIgnoreCase))) { - _outputs.Add(new TaskItem(outputFilePath)); + _outputs.Add(outputItem); } break; default: - if (Path.GetFileName(file).Equals("app.config", StringComparison.InvariantCultureIgnoreCase)) + if (fileName.Equals("app.config", StringComparison.InvariantCultureIgnoreCase)) { - _outputs.Add(new TaskItem(outputFilePath)); + _outputs.Add(outputItem); } break; } diff --git a/src/Mobile.BuildTools/Tasks/ConfigurationManagerHandlerTask.cs b/src/Mobile.BuildTools/Tasks/ConfigurationManagerHandlerTask.cs index 0943c36a..229d32be 100644 --- a/src/Mobile.BuildTools/Tasks/ConfigurationManagerHandlerTask.cs +++ b/src/Mobile.BuildTools/Tasks/ConfigurationManagerHandlerTask.cs @@ -14,121 +14,63 @@ public class ConfigurationManagerHandlerTask : BuildToolsTaskBase { public ITaskItem[] InputConfigFiles { get; set; } - public ITaskItem[] ExpectedAppConfig { get; set; } + public ITaskItem[] GeneratedAppConfig { get; set; } [Required] public string AppConfigEnvironment { get; set; } - private List _outputs = new List(); [Output] - public ITaskItem[] OutputConfigs => _outputs.ToArray(); - - public ITaskItem[] GeneratedAppConfig => ExpectedAppConfig; + public ITaskItem[] OutputConfigs => + GeneratedAppConfig?.Select(x => new TaskItem(x.ItemSpec))?.ToArray() + ?? Array.Empty(); internal override void ExecuteInternal(IBuildConfiguration config) { - //if (!Debugger.IsAttached) - // Debugger.Launch(); - - if (!InputConfigFiles.Any()) + // Validate we have inputs... + if (InputConfigFiles is null || !InputConfigFiles.Any()) { Log.LogMessage("No input config files were found"); return; } - //var configsOutputDirPath = Path.Combine(IntermediateOutputPath, "configs"); - //var configsOutputDir = new DirectoryInfo(configsOutputDirPath); - - var rootConfigFile = InputConfigFiles.FirstOrDefault(x => x.ItemSpec.Equals("app.config", StringComparison.InvariantCultureIgnoreCase)); - var transformFile = InputConfigFiles.FirstOrDefault(x => x.ItemSpec.Equals($"app.{AppConfigEnvironment}.config", StringComparison.InvariantCultureIgnoreCase)); - - if(transformFile is null) + if(GeneratedAppConfig is null || !GeneratedAppConfig.Any()) { - Log.LogMessage($"No transform config 'app.{AppConfigEnvironment.ToLower()}.config' could be found in the output directory."); + Log.LogError("Input app.config's were found, however no outputs were found."); return; } - if(rootConfigFile is null) + // Get root app.config + var rootConfigFile = GetTaskItem(InputConfigFiles, "app.config"); + if (rootConfigFile is null) { // this should never happen... - Log.LogError("We could not locate an 'app.config' in the output directory. Please file a bug."); + Log.LogError("We could not locate an 'app.config'. Please file a bug."); return; } + // Reset output directory + var fi = new FileInfo(GeneratedAppConfig.First().ItemSpec); + if(fi.Directory.Exists) + { + fi.Directory.Delete(true); + } + fi.Directory.Create(); + + // Get Transform config + var transformFile = GetTaskItem(InputConfigFiles, $"app.{AppConfigEnvironment}.config"); + var outputs = GeneratedAppConfig.Select(x => Models.Configuration.ExpectedAppConfig.FromTaskItem(x)); var generator = new ConfigurationManagerTransformationGenerator(config) { BaseConfigPath = rootConfigFile.ItemSpec, - TransformFilePath = transformFile.ItemSpec + TransformFilePath = transformFile.ItemSpec, + ExpectedConfigs = outputs }; generator.Execute(); - - _outputs.AddRange(generator.Outputs); } - //private string PrimaryTaskItemPath(bool isSanityCheck = false) - //{ - // var item = InputConfigFiles.FirstOrDefault(x => x.GetMetadata("IsRootConfig").Equals(bool.TrueString, StringComparison.InvariantCultureIgnoreCase)); - - // if(item is null) - // { - // item = InputConfigFiles.FirstOrDefault(x => Path.GetFileName(x.ItemSpec).Equals("app.config", StringComparison.InvariantCultureIgnoreCase)); - // } - - // if(item is null && !isSanityCheck) - // { - // var searchDirectories = InputConfigFiles.Select(x => new FileInfo(x.ItemSpec).Directory).Distinct(); - - // var inputFiles = new List(); - // foreach(var directory in searchDirectories) - // { - // inputFiles.AddRange(directory.EnumerateFiles("*.config").Select(x => new TaskItem(x.FullName))); - // } - // InputConfigFiles = inputFiles.ToArray(); - // return PrimaryTaskItemPath(true); - // } - - // return item?.ItemSpec; - //} - - //private IEnumerable GetOutputFiles(string outputFileDirectory) - //{ - // //int files = InputConfigFiles.Length; - // var files = InputConfigFiles.Select(x => x.ItemSpec); - // foreach (var file in files) - // { - // var fi = new FileInfo(file); - // var outputPath = Path.Combine(outputFileDirectory, Path.GetFileName(file)); - // fi.CopyTo(outputPath); - // yield return outputPath; - // //yield return CopyFile(file, outputFileDirectory); - // } - //} - - //private string CopyFile(string inputFile, string outputFileDirectory) - //{ - // var inputFileInfo = new FileInfo(inputFile); - // var fileName = Path.GetFileName(inputFile); - // var outputFile = Path.Combine(outputFileDirectory, fileName); - - // if(!File.Exists(outputFile) || File.GetLastWriteTime(outputFile) < inputFileInfo.LastWriteTime) - // { - // var contents = File.ReadAllText(inputFile); - - // Log.LogMessage($"Copying File {inputFile} to {outputFile}"); - // File.WriteAllText(outputFile, contents); - // return outputFile; - // } - - // if(File.Exists(outputFile)) - // { - // Log.LogMessage(MessageImportance.Low, $"No update necessary for {outputFile}"); - // } - // else - // { - // Log.LogError($"An expected error occurrected while attempting to copy the file {inputFile} to {outputFile}"); - // } - - // return outputFile; - //} + private static ITaskItem GetTaskItem(IEnumerable items, string expectedFileName) => + items.FirstOrDefault(x => + Path.GetFileName(x.ItemSpec).Equals(expectedFileName, StringComparison.InvariantCultureIgnoreCase) || + (!string.IsNullOrEmpty(x.GetMetadata("Link")) && Path.GetFileName(x.GetMetadata("Link")).Equals(expectedFileName, StringComparison.InvariantCultureIgnoreCase))); } } diff --git a/src/Mobile.BuildTools/iOS.targets b/src/Mobile.BuildTools/iOS.targets index 7c1e0f7b..fdf1e5cc 100644 --- a/src/Mobile.BuildTools/iOS.targets +++ b/src/Mobile.BuildTools/iOS.targets @@ -30,7 +30,7 @@ -