void TemplateProcessed(TemplateProcessed obj) { if (obj.Template.EndsWith("XamarinApp.sln")) { // we have to check based upon the SolutionIdentifier because when this file is processed the projects itself are not copied yet (or are skipped) // therefore they will be marked as NotFound, and not able to identity the type; solutionIdentifier can always be used var solution = FluentMSBuild.Use(obj.File).Solution; if (!Configuration.UseIOS) { var projects = solution.Projects.Where(q => q.SolutionIdentifier == "XamarinApp.iOS"); RemoveProjects(projects, solution); solution.RemoveConfigurations(ProjectSubType.XamarinIOS); } if (!Configuration.UseAndroid) { var projects = solution.Projects.Where(q => q.SolutionIdentifier == "XamarinApp.Android"); RemoveProjects(projects, solution); } } void RemoveProjects(IEnumerable <Project> projects, Solution solution) { foreach (var project in projects) { solution.Remove(project); } } }
public override void Execute() { // ViewModel var viewModelFile = Path.Combine(TemplatePath, "{{ Name }}.cs"); var viewModelTargetFile = Path.Combine(WorkingDirectory, TargetFileName(viewModelFile, TemplatePath)); viewModelTargetFile = FluentHandlebars.Create(this) .WithAllHelpers() .HavingModel(Configuration) .UsingFileTemplate(viewModelFile) .OutputFile(viewModelTargetFile, false); //DO NOT OVERWRITE ViewModel FluentMSBuild.Use(viewModelTargetFile) .WithBuildAction(BuildAction.Compile) .AddToProject(); // generated ViewModel var generatedViewModelFile = Path.Combine(TemplatePath, "{{ Name }}.generated.cs"); var generatedViewModelTargetFile = Path.Combine(WorkingDirectory, TargetFileName(generatedViewModelFile, TemplatePath)); generatedViewModelTargetFile = FluentHandlebars.Create(this) .WithAllHelpers() .WithCustomHelper(Helpers) .HavingModel(Configuration) .UsingFileTemplate(generatedViewModelFile) .OutputFile(generatedViewModelTargetFile, true); FluentMSBuild.Use(generatedViewModelTargetFile) .WithBuildAction(BuildAction.Compile) .WithDependencyOn(viewModelTargetFile) .AddToProject(); FluentMSBuild.Use(InputFileName) .WithBuildAction(BuildAction.None) .WithDependencyOn(viewModelTargetFile) .AddToProject(); }
public override void Execute() { // get and write asset log var assetLogFile = Path.Combine(WorkingDirectory, "assets-log.json"); if (!File.Exists(assetLogFile)) { File.WriteAllText(assetLogFile, string.Empty); } var assetLogs = new List <AssetLog>(); var assetLogsContents = File.ReadAllText(assetLogFile); if (!string.IsNullOrEmpty(assetLogsContents)) { assetLogs = assetLogsContents.FromJson <List <AssetLog> >(); } var needToUpdateLog = false; if (!string.IsNullOrEmpty(Configuration.AssetDefault?.Pattern)) { var files = Directory.GetFiles(Path.Combine(WorkingDirectory, Configuration.AssetsPath), Configuration.AssetDefault.Pattern); foreach (var file in files) { var fileName = new FileInfo(file).Name; if (Configuration.Assets.FirstOrDefault(q => q.File == fileName) == null) { Configuration.Assets.Add(new AssetClass() { File = fileName }); } } } if (!string.IsNullOrEmpty(Configuration.AssetDefault?.Size)) { foreach (var asset in Configuration.Assets) { if (string.IsNullOrEmpty(asset.Size)) { asset.Size = Configuration.AssetDefault.Size; } } } foreach (var asset in Configuration.Assets) { var path = Path.Combine(WorkingDirectory, Configuration.AssetsPath, asset.File); if (!File.Exists(path)) { Log.Error($"Could not find {asset.File} in {Configuration.AssetsPath}"); continue; } var assetFileInfo = new FileInfo(path); var found = assetLogs.FirstOrDefault(a => a.AssetFile == asset.File); if (found == null) { assetLogs.Add(new AssetLog() { AssetFile = asset.File, LastUpdated = assetFileInfo.LastWriteTimeUtc, FileSize = assetFileInfo.Length, Size = asset.Size }); } else { if (found.FileSize == assetFileInfo.Length && found.LastUpdated == assetFileInfo.LastWriteTimeUtc && found.Size == asset.Size) { Log.Debug($"Skipping {asset.File} - No Change"); continue; } found.LastUpdated = assetFileInfo.LastWriteTimeUtc; found.FileSize = assetFileInfo.Length; found.Size = asset.Size; } needToUpdateLog = true; Log.Information($"Processing {asset.File}"); var svg = new SKSvg(); svg.Load(path); var sourceActualWidth = svg.Picture.CullRect.Width; var sourceActualHeight = svg.Picture.CullRect.Height; var wantedBaseWidth = 0.0F; var wantedBaseHeight = 0.0F; if (!string.IsNullOrEmpty(asset.Size)) { var sizeParts = asset.Size.Split('x'); if (sizeParts.Length == 2) { var widthOk = float.TryParse(sizeParts[0], NumberStyles.Integer, new CultureInfo("en-US"), out wantedBaseWidth); var heightOk = float.TryParse(sizeParts[1], NumberStyles.Integer, new CultureInfo("en-US"), out wantedBaseHeight); if (!widthOk) { wantedBaseWidth = sourceActualWidth; } if (!heightOk) { wantedBaseHeight = sourceActualHeight; } } } else { wantedBaseWidth = sourceActualWidth; wantedBaseHeight = sourceActualHeight; } var nominalRatio = Math.Max((double)wantedBaseWidth / (double)sourceActualWidth, (double)wantedBaseHeight / (double)sourceActualHeight); foreach (var platform in Configuration.Platforms) { var configs = new List <Output>(); switch (platform.Type) { case Platforms.iOS: configs.AddRange(new[] { new Output() { Ratio = 1 }, new Output() { Ratio = 2, Suffix = "@2x" }, new Output() { Ratio = 3, Suffix = "@3x" } }); break; case Platforms.Android: { var folderPrefix = "mipmap"; if (platform.AndroidOptions != null && !string.IsNullOrEmpty(platform.AndroidOptions.AssetFolderPrefix.ToString())) { folderPrefix = platform.AndroidOptions.AssetFolderPrefix.ToString(); } configs.AddRange(new[] { new Output() { Ratio = 1.0, Path = $"{folderPrefix}-mdpi" }, new Output() { Ratio = 1.5, Path = $"{folderPrefix}-hdpi" }, new Output() { Ratio = 2, Path = $"{folderPrefix}-xhdpi" }, new Output() { Ratio = 3, Path = $"{folderPrefix}-xxhdpi" }, new Output() { Ratio = 4, Path = $"{folderPrefix}-xxxhdpi" }, }); break; } case Platforms.UWP: configs.AddRange(new[] { new Output() { Ratio = 1.0, Suffix = ".scale-100" }, new Output() { Ratio = 1.25, Suffix = ".scale-125" }, new Output() { Ratio = 1.50, Suffix = ".scale-150" }, new Output() { Ratio = 2, Suffix = ".scale-200" }, new Output() { Ratio = 4, Suffix = ".scale-400" }, }); break; default: throw new ArgumentOutOfRangeException(); } foreach (var config in configs) { var destinationFolder = "Resources"; if (platform.Type == Platforms.UWP) { destinationFolder = "Assets"; } var destinationFile = Path.Combine(WorkingDirectory, platform.ProjectPath, destinationFolder); if (string.IsNullOrEmpty(config.Path)) { destinationFile = Path.Combine(destinationFile, $"{asset.GetSafeFile().Replace(".svg", "")}{config.Suffix}.png"); } else { destinationFile = Path.Combine(destinationFile, config.Path, $"{asset.GetSafeFile().Replace(".svg", ".png")}"); } var destinationPath = new FileInfo(destinationFile); if (!Directory.Exists(destinationPath.DirectoryName)) { Directory.CreateDirectory(destinationPath.DirectoryName); } var resizeRatio = config.Ratio; var adjustRatio = nominalRatio * resizeRatio; var scaledWidth = sourceActualWidth * adjustRatio; var scaledHeight = sourceActualHeight * adjustRatio; var bmp = new SKBitmap((int)scaledWidth, (int)scaledHeight); var canvas = new SKCanvas(bmp); // Make a matrix to scale the SVG var matrix = SKMatrix.MakeScale((float)adjustRatio, (float)adjustRatio); canvas.Clear(SKColors.Transparent); // Draw the svg onto the canvas with our scaled matrix canvas.DrawPicture(svg.Picture, ref matrix); // Save the op canvas.Save(); // Export the canvas var img = SKImage.FromBitmap(bmp); var data = img.Encode(SKEncodedImageFormat.Png, 100); using (var fs = File.Open(destinationFile, FileMode.Create)) { data.SaveTo(fs); } switch (platform.Type) { case Platforms.iOS: FluentMSBuild.Use(destinationFile).WithBuildAction(BuildAction.BundleResource).AddToProject(); break; case Platforms.Android: FluentMSBuild.Use(destinationFile).WithBuildAction(BuildAction.AndroidResource).AddToProject(); break; case Platforms.UWP: FluentMSBuild.Use(destinationFile).WithBuildAction(BuildAction.Content).AddToProject(); break; } } } } if (needToUpdateLog) { File.WriteAllText(assetLogFile, assetLogs.ToJson()); } }
public override void Execute() { JsConfig.IncludeNullValues = false; JsConfig.EmitCamelCaseNames = true; foreach (var platform in Configuration.Platforms) { var fileName = Path.Combine(WorkingDirectory, platform.IconFileName); var fileInfo = new FileInfo(fileName); if (!File.Exists(fileInfo.FullName)) { Log.Error($"Icon not found ({fileInfo.FullName})"); continue; } var svg = SkiaHelper.Load(fileInfo.FullName); var sourceActualWidth = svg.Picture.CullRect.Width; var sourceActualHeight = svg.Picture.CullRect.Height; var assets = new List <ExportAsset>(); var platformPath = new DirectoryInfo(Path.Combine(WorkingDirectory, platform.ProjectPath)); if (!platformPath.Exists) { Log.Error($"Platform path for {platform.Type} not found ({platformPath.FullName})"); continue; } switch (platform.Type) { case Platforms.iOS: { var targetPath = Path.Combine(platformPath.FullName, "Assets.xcassets", "AppIcon.appiconset"); var sizes = new[] { 20, 29, 40, 58, 60, 76, 80, 87, 120, 152, 167, 180, 1024 }; assets.AddRange(sizes.Select(size => new ExportAsset(fileInfo.FullName, targetPath, $"Icon-App-{size}x{size}.png", sourceActualWidth, sourceActualHeight, size, size))); break; } case Platforms.AppleWatch: { var targetPath = Path.Combine(platformPath.FullName, "Assets.xcassets", "AppIcon.appiconset"); var sizes = new[] { 48, 55, 58, 80, 87, 88, 172, 196 }; assets.AddRange(sizes.Select(size => new ExportAsset(fileInfo.FullName, targetPath, $"Icon-App-{size}x{size}.png", sourceActualWidth, sourceActualHeight, size, size))); break; } case Platforms.MacOs: { var targetPath = Path.Combine(platformPath.FullName, "Assets.xcassets", "AppIcon.appiconset"); var sizes = new[] { 16, 32, 64, 128, 256, 512, 1024 }; assets.AddRange(sizes.Select(size => new ExportAsset(fileInfo.FullName, targetPath, $"Icon-App-{size}x{size}.png", sourceActualWidth, sourceActualHeight, size, size))); break; } case Platforms.Android: { var folderPrefix = "mipmap"; if (platform.AndroidOptions != null && !string.IsNullOrEmpty(platform.AndroidOptions.AssetFolderPrefix.ToString())) { folderPrefix = platform.AndroidOptions.AssetFolderPrefix.ToString(); } assets.Add(new ExportAsset(fileInfo.FullName, Path.Combine(platformPath.FullName, "Resources", $"{folderPrefix}-mdpi"), "Icon.png", sourceActualWidth, sourceActualHeight, 48, 48)); assets.Add(new ExportAsset(fileInfo.FullName, Path.Combine(platformPath.FullName, "Resources", $"{folderPrefix}-hdpi"), "Icon.png", sourceActualWidth, sourceActualHeight, 72, 72)); assets.Add(new ExportAsset(fileInfo.FullName, Path.Combine(platformPath.FullName, "Resources", $"{folderPrefix}-xhdpi"), "Icon.png", sourceActualWidth, sourceActualHeight, 96, 96)); assets.Add(new ExportAsset(fileInfo.FullName, Path.Combine(platformPath.FullName, "Resources", $"{folderPrefix}-xxhdpi"), "Icon.png", sourceActualWidth, sourceActualHeight, 144, 144)); assets.Add(new ExportAsset(fileInfo.FullName, Path.Combine(platformPath.FullName, "Resources", $"{folderPrefix}-xxxhdpi"), "Icon.png", sourceActualWidth, sourceActualHeight, 192, 192)); break; } case Platforms.UWP: { // https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/app-assets#asset-size-tables var targetPath = Path.Combine(platformPath.FullName, "Assets"); var sizes = new[] { 71, 150, 310, 44 }; var scales = new[] { 100, 125, 150, 200, 400 }; foreach (var size in sizes) { assets.AddRange(scales.Select(scale => new ExportAsset(fileInfo.FullName, targetPath, $"Square{size}x{size}Logo.scale-{scale}.png", sourceActualWidth, sourceActualHeight, size, size))); } break; } default: throw new ArgumentOutOfRangeException(); } foreach (var asset in assets) { var bmp = new SKBitmap((int)asset.ScaledWidth, (int)asset.ScaledHeight); var canvas = new SKCanvas(bmp); var matrix = SKMatrix.MakeScale((float)asset.Ratio, (float)asset.Ratio); canvas.Clear(SKColors.Transparent); canvas.DrawPicture(svg.Picture, ref matrix); canvas.Save(); // Export the canvas var img = SKImage.FromBitmap(bmp); var data = img.Encode(SKEncodedImageFormat.Png, 100); asset.FileName.EnsureFolderExists(); using (var fs = File.Open(asset.FileName, FileMode.Create)) { Log.Information($"Writing {asset.FileName}"); data.SaveTo(fs); } var platformProjectFolder = Path.Combine(WorkingDirectory, platform.ProjectPath); var destinationFolder = "Resources"; if (platform.Type == Platforms.UWP) { destinationFolder = "Assets"; } var destinationFile = Path.Combine(WorkingDirectory, platform.ProjectPath, destinationFolder, asset.FileName); switch (platform.Type) { case Platforms.iOS: case Platforms.AppleWatch: case Platforms.MacOs: FluentMSBuild.Use(destinationFile).WithBuildAction(BuildAction.ImageAsset).AddToProject(); break; case Platforms.Android: FluentMSBuild.Use(destinationFile).WithBuildAction(BuildAction.AndroidResource).AddToProject(); break; case Platforms.UWP: FluentMSBuild.Use(destinationFile).WithBuildAction(BuildAction.Content).AddToProject(); break; } } if (platform.Type == Platforms.iOS || platform.Type == Platforms.AppleWatch || platform.Type == Platforms.MacOs) { var contentsFile = Path.Combine(platformPath.FullName, "Assets.xcassets", "AppIcon.appiconset", "Contents.json"); Log.Information($"Writing {contentsFile}"); var contents = File.ReadAllText(contentsFile).FromJson <XcodeContents>(); foreach (var contentsImage in contents.Images) { var process = false; switch (platform.Type) { case Platforms.iOS when contentsImage.Idiom == "iphone" || contentsImage.Idiom == "ipad" || contentsImage.Idiom == "ios-marketing": case Platforms.AppleWatch when contentsImage.Idiom == "watch": case Platforms.MacOs when contentsImage.Idiom == "mac": process = true; break; } if (process) { // calculate needed size var size = double.Parse(contentsImage.Size.Split('x')[0], NumberStyles.Float, new CultureInfo("en-US")); var scale = double.Parse(contentsImage.Scale.Substring(0, 1)); var foundSize = assets.FirstOrDefault(a => a.ScaledWidth == size * scale); if (foundSize != null) { contentsImage.Filename = new FileInfo(foundSize.FileName).Name; } } } File.WriteAllText(contentsFile, contents.ToJson().IndentJson()); } } }
protected override int Execute() { base.Execute(); // find csproj var csproj = Directory.GetFiles(WorkingDirectory, "*.csproj", SearchOption.AllDirectories).FirstOrDefault(); if (csproj == null) { Log.Fatal($"Could not find .csproj in underlying folders of {WorkingDirectory}"); } // increase var addBuild = !(!BuildOption.HasValue() && (MajorOption.HasValue() || MinorOption.HasValue())); FluentMSBuild.Use(csproj).IncrementVersion(MajorOption.HasValue(), MinorOption.HasValue(), addBuild); // Creating temporary folder var tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Log.Debug($"Creating temp folder {tempFolder}"); Directory.CreateDirectory(tempFolder); Log.Information("Packing generator..."); DotNetRunner.Pack(tempFolder); var nupkg = Directory.GetFiles(tempFolder, "*.nupkg").FirstOrDefault(); var nupkgFile = new FileInfo(nupkg).Name; Log.Information($"Pushing generator {nupkgFile}"); var source = string.Empty; var apiKey = string.Empty; if (SourceOption.HasValue()) { source = SourceOption.ParsedValue.FromEnvironmentOrDefault(); if (string.IsNullOrEmpty(source)) { Log.Fatal("When specifying --source your need to add a valid source (--source https://{YourUrl})"); } } if (ApiKeyOption.HasValue()) { apiKey = ApiKeyOption.ParsedValue.FromEnvironmentOrDefault(); if (string.IsNullOrEmpty(apiKey)) { Log.Fatal("When specifying --apikey your need to add a valid api key (--apikey YourKey"); } } var nugetPush = DotNetRunner.NugetPush(nupkg, source, apiKey); if (nugetPush == 0) { Log.Information("Package was pushed"); DotNetRunner.InstallOrUpdateLocal(nupkgFile, tempFolder); Log.Information("Genyman generator was installed locally"); } else { Log.Information("Skipping local installation of Genyman generator - Nuget Failed"); } // Cleanup var files = Directory.GetFiles(tempFolder); foreach (var file in files) { Log.Debug($"Cleanup. Deleting file {file}"); File.Delete(file); } Log.Debug($"Cleanup. Deleting folder {tempFolder}"); Directory.Delete(tempFolder); return(0); }