public void TestMergeArrays() { var plist = new PDictionary(); var array0 = new PArray(); array0.Add("item0"); array0.Add("item1"); array0.Add("item2"); array0.Add("item3"); plist.Add("CFBundleIdentifier", "com.microsoft.merge-arrays"); plist.Add("CFArrayItems", array0); var array1 = new PArray(); array1.Add("item2"); array1.Add("item3"); var expected = (PDictionary)plist.Clone(); array0.RemoveAt(3); array0.RemoveAt(2); var tmp = Path.Combine(Cache.CreateTemporaryDirectory(), "tmpfile"); array1.Save(tmp); TestExecuteTask(plist, PropertyListEditorAction.Merge, ":CFArrayItems", null, tmp, expected); }
public void TestMergeArrays() { var plist = new PDictionary(); var array0 = new PArray(); array0.Add("item0"); array0.Add("item1"); array0.Add("item2"); array0.Add("item3"); plist.Add("CFBundleIdentifier", "com.microsoft.merge-arrays"); plist.Add("CFArrayItems", array0); var array1 = new PArray(); array1.Add("item2"); array1.Add("item3"); var expected = (PDictionary)plist.Clone(); array0.RemoveAt(3); array0.RemoveAt(2); var tmp = Path.GetTempFileName(); array1.Save(tmp); try { TestExecuteTask(plist, PropertyListEditorAction.Merge, ":CFArrayItems", null, tmp, expected); } finally { File.Delete(tmp); } }
public override bool Execute() { var intermediate = Path.Combine(IntermediateOutputPath, ToolName); var intermediateBundleDir = Path.Combine(intermediate, "bundle"); var intermediateCloneDir = Path.Combine(intermediate, "cloned-assets"); var manifest = new TaskItem(Path.Combine(intermediate, "asset-manifest.plist")); var bundleResources = new List <ITaskItem> (); var outputManifests = new List <ITaskItem> (); var catalogs = new List <ITaskItem> (); var unique = new HashSet <string> (); string bundleIdentifier = null; var knownSpecs = new HashSet <string> (); var clones = new HashSet <string> (); var items = new List <ITaskItem> (); var specs = new PArray(); Log.LogTaskName("ACTool"); Log.LogTaskProperty("AppManifest", AppManifest); Log.LogTaskProperty("DeviceModel", DeviceModel); Log.LogTaskProperty("DeviceOSVersion", DeviceOSVersion); Log.LogTaskProperty("EnableOnDemandResources", EnableOnDemandResources); Log.LogTaskProperty("ImageAssets", ImageAssets); Log.LogTaskProperty("IntermediateOutputPath", IntermediateOutputPath); Log.LogTaskProperty("IsWatchApp", IsWatchApp); Log.LogTaskProperty("OptimizePNGs", OptimizePNGs); Log.LogTaskProperty("OutputPath", OutputPath); Log.LogTaskProperty("ProjectDir", ProjectDir); Log.LogTaskProperty("ResourcePrefix", ResourcePrefix); Log.LogTaskProperty("SdkBinPath", SdkBinPath); Log.LogTaskProperty("SdkPlatform", SdkPlatform); Log.LogTaskProperty("SdkVersion", SdkVersion); switch (SdkPlatform) { case "iPhoneSimulator": case "iPhoneOS": case "MacOSX": case "WatchSimulator": case "WatchOS": case "AppleTVSimulator": case "AppleTVOS": break; default: Log.LogError("Unrecognized platform: {0}", SdkPlatform); return(false); } if (AppManifest != null) { try { plist = PDictionary.FromFile(AppManifest.ItemSpec); } catch (Exception ex) { Log.LogError(null, null, null, AppManifest.ItemSpec, 0, 0, 0, 0, "{0}", ex.Message); return(false); } bundleIdentifier = plist.GetCFBundleIdentifier(); } for (int i = 0; i < ImageAssets.Length; i++) { var vpath = BundleResource.GetVirtualProjectPath(ProjectDir, ImageAssets[i], !string.IsNullOrEmpty(SessionId)); // Ignore MacOS .DS_Store files... if (Path.GetFileName(vpath).Equals(".DS_Store", StringComparison.OrdinalIgnoreCase)) { continue; } // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, etc) var catalog = Path.GetDirectoryName(vpath); // keep walking up the directory structure until we get to the .xcassets directory while (!string.IsNullOrEmpty(catalog) && Path.GetExtension(catalog) != ".xcassets") { catalog = Path.GetDirectoryName(catalog); } if (string.IsNullOrEmpty(catalog)) { Log.LogWarning(null, null, null, ImageAssets[i].ItemSpec, 0, 0, 0, 0, "Asset not part of an asset catalog: {0}", ImageAssets[i].ItemSpec); continue; } if (ImageAssets[i].GetMetadata("Link") != null) { // Note: if any of the files within a catalog are linked, we'll have to clone the *entire* catalog clones.Add(catalog); continue; } // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these if (Path.GetFileName(vpath) != "Contents.json") { continue; } items.Add(ImageAssets[i]); } // clone any *.xcassets dirs that need cloning if (clones.Count > 0) { if (Directory.Exists(intermediateCloneDir)) { Directory.Delete(intermediateCloneDir, true); } Directory.CreateDirectory(intermediateCloneDir); items.Clear(); for (int i = 0; i < ImageAssets.Length; i++) { var vpath = BundleResource.GetVirtualProjectPath(ProjectDir, ImageAssets[i], !string.IsNullOrEmpty(SessionId)); var clone = false; ITaskItem item; // Ignore MacOS .DS_Store files... if (Path.GetFileName(vpath).Equals(".DS_Store", StringComparison.OrdinalIgnoreCase)) { continue; } foreach (var catalog in clones) { if (vpath.Length > catalog.Length && vpath[catalog.Length] == '/' && vpath.StartsWith(catalog, StringComparison.Ordinal)) { clone = true; break; } } if (clone) { var src = ImageAssets[i].GetMetadata("FullPath"); if (!File.Exists(src)) { Log.LogError(null, null, null, src, 0, 0, 0, 0, "File not found: {0}", src); return(false); } var dest = Path.Combine(intermediateCloneDir, vpath); var dir = Path.GetDirectoryName(dest); Directory.CreateDirectory(dir); File.Copy(src, dest, true); // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these if (Path.GetFileName(vpath) != "Contents.json") { continue; } item = new TaskItem(dest); ImageAssets[i].CopyMetadataTo(item); item.SetMetadata("Link", vpath); } else { // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these if (Path.GetFileName(vpath) != "Contents.json") { continue; } item = ImageAssets[i]; } items.Add(item); } } // Note: `items` contains only the Contents.json files at this point for (int i = 0; i < items.Count; i++) { var vpath = BundleResource.GetVirtualProjectPath(ProjectDir, items[i], !string.IsNullOrEmpty(SessionId)); var path = items[i].GetMetadata("FullPath"); // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, etc) var catalog = Path.GetDirectoryName(vpath); path = Path.GetDirectoryName(path); // keep walking up the directory structure until we get to the .xcassets directory while (!string.IsNullOrEmpty(catalog) && Path.GetExtension(catalog) != ".xcassets") { catalog = Path.GetDirectoryName(catalog); path = Path.GetDirectoryName(path); } if (unique.Add(catalog)) { var item = new TaskItem(path); item.SetMetadata("Link", catalog); catalogs.Add(item); } if (AppleSdkSettings.XcodeVersion.Major >= 7 && !string.IsNullOrEmpty(bundleIdentifier) && SdkPlatform != "WatchSimulator") { var text = File.ReadAllText(items[i].ItemSpec); if (string.IsNullOrEmpty(text)) { continue; } var json = JsonConvert.DeserializeObject(text) as JObject; if (json == null) { continue; } var properties = json.Property("properties"); if (properties == null) { continue; } var resourceTags = properties.Value.ToObject <JObject> ().Property("on-demand-resource-tags"); if (resourceTags == null || resourceTags.Value.Type != JTokenType.Array) { continue; } var tagArray = resourceTags.Value.ToObject <JArray> (); var tags = new HashSet <string> (); string hash; foreach (var tag in tagArray.Select(token => token.ToObject <string> ())) { tags.Add(tag); } var tagList = tags.ToList(); tagList.Sort(); var assetDir = AssetPackUtils.GetAssetPackDirectory(intermediate, bundleIdentifier, tagList, out hash); if (knownSpecs.Add(hash)) { var assetpack = new PDictionary(); var ptags = new PArray(); Directory.CreateDirectory(assetDir); for (int j = 0; j < tagList.Count; j++) { ptags.Add(new PString(tagList[j])); } assetpack.Add("bundle-id", new PString(string.Format("{0}.asset-pack-{1}", bundleIdentifier, hash))); assetpack.Add("bundle-path", new PString(Path.GetFullPath(assetDir))); assetpack.Add("tags", ptags); specs.Add(assetpack); } } } if (catalogs.Count == 0) { // There are no (supported?) asset catalogs return(true); } partialAppManifest = new TaskItem(Path.Combine(intermediate, "partial-info.plist")); if (specs.Count > 0) { outputSpecs = Path.Combine(intermediate, "output-specifications.plist"); specs.Save(outputSpecs, true); } Directory.CreateDirectory(intermediateBundleDir); // Note: Compile() will set the PartialAppManifest property if it is used... if ((Compile(catalogs.ToArray(), intermediateBundleDir, manifest)) != 0) { return(false); } if (PartialAppManifest != null && !File.Exists(PartialAppManifest.GetMetadata("FullPath"))) { Log.LogError("Partial Info.plist file was not generated: {0}", PartialAppManifest.GetMetadata("FullPath")); } try { var manifestOutput = PDictionary.FromFile(manifest.ItemSpec); LogWarningsAndErrors(manifestOutput, catalogs[0]); bundleResources.AddRange(GetCompiledBundleResources(manifestOutput, intermediateBundleDir)); outputManifests.Add(manifest); } catch (Exception ex) { Log.LogError("Failed to load output manifest for {0} for the file {2}: {1}", ToolName, ex.Message, manifest.ItemSpec); } foreach (var assetpack in specs.OfType <PDictionary> ()) { var path = Path.Combine(assetpack.GetString("bundle-path").Value, "Info.plist"); var bundlePath = PathUtils.AbsoluteToRelative(intermediate, path); var outputPath = Path.Combine(OutputPath, bundlePath); var rpath = Path.Combine(intermediate, bundlePath); var dict = new PDictionary(); dict.SetCFBundleIdentifier(assetpack.GetString("bundle-id").Value); dict.Add("Tags", assetpack.GetArray("tags").Clone()); dict.Save(path, true, true); var item = new TaskItem(rpath); item.SetMetadata("LogicalName", bundlePath); item.SetMetadata("OutputPath", outputPath); item.SetMetadata("Optimize", "false"); bundleResources.Add(item); } BundleResources = bundleResources.ToArray(); OutputManifests = outputManifests.ToArray(); return(!Log.HasLoggedErrors); }
public override bool Execute() { var intermediate = Path.Combine(IntermediateOutputPath, ToolName); var intermediateBundleDir = Path.Combine(intermediate, "bundle"); var intermediateCloneDir = Path.Combine(intermediate, "cloned-assets"); var manifest = new TaskItem(Path.Combine(intermediate, "asset-manifest.plist")); var bundleResources = new List <ITaskItem> (); var outputManifests = new List <ITaskItem> (); var catalogs = new List <ITaskItem> (); var unique = new HashSet <string> (); var knownSpecs = new HashSet <string> (); var clones = new HashSet <string> (); var items = new List <ITaskItem> (); var specs = new PArray(); if (AppManifest != null) { try { plist = PDictionary.FromFile(AppManifest.ItemSpec); } catch (Exception ex) { Log.LogError(null, null, null, AppManifest.ItemSpec, 0, 0, 0, 0, "{0}", ex.Message); return(false); } } for (int i = 0; i < ImageAssets.Length; i++) { var vpath = BundleResource.GetVirtualProjectPath(ProjectDir, ImageAssets[i], !string.IsNullOrEmpty(SessionId)); // Ignore MacOS .DS_Store files... if (Path.GetFileName(vpath).Equals(".DS_Store", StringComparison.OrdinalIgnoreCase)) { continue; } // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, etc) var catalog = Path.GetDirectoryName(vpath); // keep walking up the directory structure until we get to the .xcassets directory while (!string.IsNullOrEmpty(catalog) && Path.GetExtension(catalog) != ".xcassets") { catalog = Path.GetDirectoryName(catalog); } if (string.IsNullOrEmpty(catalog)) { Log.LogWarning(null, null, null, ImageAssets[i].ItemSpec, 0, 0, 0, 0, MSBStrings.W0090, ImageAssets[i].ItemSpec); continue; } if (ImageAssets[i].GetMetadata("Link") != null) { // Note: if any of the files within a catalog are linked, we'll have to clone the *entire* catalog clones.Add(catalog); continue; } // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these if (Path.GetFileName(vpath) != "Contents.json") { continue; } items.Add(ImageAssets[i]); } // clone any *.xcassets dirs that need cloning if (clones.Count > 0) { if (Directory.Exists(intermediateCloneDir)) { Directory.Delete(intermediateCloneDir, true); } Directory.CreateDirectory(intermediateCloneDir); items.Clear(); for (int i = 0; i < ImageAssets.Length; i++) { var vpath = BundleResource.GetVirtualProjectPath(ProjectDir, ImageAssets[i], !string.IsNullOrEmpty(SessionId)); var clone = false; ITaskItem item; // Ignore MacOS .DS_Store files... if (Path.GetFileName(vpath).Equals(".DS_Store", StringComparison.OrdinalIgnoreCase)) { continue; } foreach (var catalog in clones) { if (vpath.Length > catalog.Length && vpath[catalog.Length] == '/' && vpath.StartsWith(catalog, StringComparison.Ordinal)) { clone = true; break; } } if (clone) { var src = ImageAssets[i].GetMetadata("FullPath"); if (!File.Exists(src)) { Log.LogError(null, null, null, src, 0, 0, 0, 0, MSBStrings.E0091, src); return(false); } var dest = Path.Combine(intermediateCloneDir, vpath); var dir = Path.GetDirectoryName(dest); Directory.CreateDirectory(dir); File.Copy(src, dest, true); // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these if (Path.GetFileName(vpath) != "Contents.json") { continue; } item = new TaskItem(dest); ImageAssets[i].CopyMetadataTo(item); item.SetMetadata("Link", vpath); } else { // filter out everything except paths containing a Contents.json file since our main processing loop only cares about these if (Path.GetFileName(vpath) != "Contents.json") { continue; } item = ImageAssets[i]; } items.Add(item); } } // Note: `items` contains only the Contents.json files at this point for (int i = 0; i < items.Count; i++) { var vpath = BundleResource.GetVirtualProjectPath(ProjectDir, items[i], !string.IsNullOrEmpty(SessionId)); var path = items[i].GetMetadata("FullPath"); // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, etc) var catalog = Path.GetDirectoryName(vpath); path = Path.GetDirectoryName(path); // keep walking up the directory structure until we get to the .xcassets directory while (!string.IsNullOrEmpty(catalog) && Path.GetExtension(catalog) != ".xcassets") { catalog = Path.GetDirectoryName(catalog); path = Path.GetDirectoryName(path); } if (unique.Add(catalog)) { var item = new TaskItem(path); item.SetMetadata("Link", catalog); catalogs.Add(item); } if (AppleSdkSettings.XcodeVersion.Major >= 7 && SdkPlatform != "WatchSimulator") { var text = File.ReadAllText(items[i].ItemSpec); if (string.IsNullOrEmpty(text)) { continue; } JsonDocument json; JsonElement value; try { var options = new JsonDocumentOptions() { AllowTrailingCommas = true, }; json = JsonDocument.Parse(text, options); } catch (JsonException je) { var line = (int)(je.LineNumber + 1 ?? 0); var col = (int)(je.BytePositionInLine + 1 ?? 0); Log.LogError(null, null, null, items [i].ItemSpec, line, col, line, col, "{0}", je.Message); return(false); } catch (Exception e) { Log.LogError(null, null, null, items[i].ItemSpec, 0, 0, 0, 0, MSBStrings.E0092, e.Message); return(false); } if (!json.RootElement.TryGetProperty("properties", out value) || value.ValueKind != JsonValueKind.Object) { continue; } var properties = value; if (!properties.TryGetProperty("on-demand-resource-tags", out value) || value.ValueKind != JsonValueKind.Array) { continue; } var resourceTags = value; var tags = new HashSet <string> (); string hash; foreach (var tag in resourceTags.EnumerateArray()) { if (tag.ValueKind == JsonValueKind.String) { tags.Add(tag.GetString()); } } var tagList = tags.ToList(); tagList.Sort(); var assetDir = AssetPackUtils.GetAssetPackDirectory(intermediate, BundleIdentifier, tagList, out hash); if (knownSpecs.Add(hash)) { var assetpack = new PDictionary(); var ptags = new PArray(); Directory.CreateDirectory(assetDir); for (int j = 0; j < tagList.Count; j++) { ptags.Add(new PString(tagList[j])); } assetpack.Add("bundle-id", new PString(string.Format("{0}.asset-pack-{1}", BundleIdentifier, hash))); assetpack.Add("bundle-path", new PString(Path.GetFullPath(assetDir))); assetpack.Add("tags", ptags); specs.Add(assetpack); } } } if (catalogs.Count == 0) { // There are no (supported?) asset catalogs return(!Log.HasLoggedErrors); } partialAppManifest = new TaskItem(Path.Combine(intermediate, "partial-info.plist")); if (specs.Count > 0) { outputSpecs = Path.Combine(intermediate, "output-specifications.plist"); specs.Save(outputSpecs, true); } Directory.CreateDirectory(intermediateBundleDir); // Note: Compile() will set the PartialAppManifest property if it is used... if ((Compile(catalogs.ToArray(), intermediateBundleDir, manifest)) != 0) { return(false); } if (PartialAppManifest != null && !File.Exists(PartialAppManifest.GetMetadata("FullPath"))) { Log.LogError(MSBStrings.E0093, PartialAppManifest.GetMetadata("FullPath")); } try { var manifestOutput = PDictionary.FromFile(manifest.ItemSpec); LogWarningsAndErrors(manifestOutput, catalogs[0]); bundleResources.AddRange(GetCompiledBundleResources(manifestOutput, intermediateBundleDir)); outputManifests.Add(manifest); } catch (Exception ex) { Log.LogError(MSBStrings.E0094, ToolName, manifest.ItemSpec, ex.Message); } foreach (var assetpack in specs.OfType <PDictionary> ()) { var path = Path.Combine(assetpack.GetString("bundle-path").Value, "Info.plist"); var bundlePath = PathUtils.AbsoluteToRelative(intermediate, path); var outputPath = Path.Combine(OutputPath, bundlePath); var rpath = Path.Combine(intermediate, bundlePath); var dict = new PDictionary(); dict.SetCFBundleIdentifier(assetpack.GetString("bundle-id").Value); dict.Add("Tags", assetpack.GetArray("tags").Clone()); dict.Save(path, true, true); var item = new TaskItem(rpath); item.SetMetadata("LogicalName", bundlePath); item.SetMetadata("OutputPath", outputPath); item.SetMetadata("Optimize", "false"); bundleResources.Add(item); } BundleResources = bundleResources.ToArray(); OutputManifests = outputManifests.ToArray(); return(!Log.HasLoggedErrors); }
public override bool Execute() { var intermediate = Path.Combine(IntermediateOutputPath, ToolName); var intermediateBundleDir = Path.Combine(intermediate, "bundle"); var manifest = new TaskItem(Path.Combine(intermediate, "asset-manifest.plist")); var bundleResources = new List <ITaskItem> (); var outputManifests = new List <ITaskItem> (); var catalogs = new List <ITaskItem> (); var unique = new HashSet <string> (); string bundleIdentifier = null; var knownSpecs = new HashSet <string> (); var specs = new PArray(); int rc; Log.LogTaskName("ACTool"); Log.LogTaskProperty("AppManifest", AppManifest); Log.LogTaskProperty("DeviceModel", DeviceModel); Log.LogTaskProperty("DeviceOSVersion", DeviceOSVersion); Log.LogTaskProperty("ImageAssets", ImageAssets); Log.LogTaskProperty("IntermediateOutputPath", IntermediateOutputPath); Log.LogTaskProperty("IsWatchApp", IsWatchApp); Log.LogTaskProperty("OptimizePNGs", OptimizePNGs); Log.LogTaskProperty("OutputPath", OutputPath); Log.LogTaskProperty("ProjectDir", ProjectDir); Log.LogTaskProperty("ResourcePrefix", ResourcePrefix); Log.LogTaskProperty("SdkBinPath", SdkBinPath); Log.LogTaskProperty("SdkPlatform", SdkPlatform); Log.LogTaskProperty("SdkVersion", SdkVersion); switch (SdkPlatform) { case "iPhoneSimulator": case "iPhoneOS": case "MacOSX": case "WatchSimulator": case "WatchOS": case "AppleTVSimulator": case "AppleTVOS": break; default: Log.LogError("Unrecognized platform: {0}", SdkPlatform); return(false); } if (AppManifest != null) { try { plist = PDictionary.FromFile(AppManifest.ItemSpec); } catch (Exception ex) { Log.LogError(null, null, null, AppManifest.ItemSpec, 0, 0, 0, 0, "{0}", ex.Message); return(false); } bundleIdentifier = plist.GetCFBundleIdentifier(); } foreach (var asset in ImageAssets) { var vpath = BundleResource.GetVirtualProjectPath(ProjectDir, asset); if (Path.GetFileName(vpath) != "Contents.json") { continue; } // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, etc) var catalog = Path.GetDirectoryName(vpath); // keep walking up the directory structure until we get to the .xcassets directory while (!string.IsNullOrEmpty(catalog) && Path.GetExtension(catalog) != ".xcassets") { catalog = Path.GetDirectoryName(catalog); } if (string.IsNullOrEmpty(catalog)) { Log.LogWarning(null, null, null, asset.ItemSpec, 0, 0, 0, 0, "Asset not part of an asset catalog: {0}", asset.ItemSpec); continue; } if (unique.Add(catalog)) { catalogs.Add(new TaskItem(catalog)); } if (AppleSdkSettings.XcodeVersion.Major >= 7 && !string.IsNullOrEmpty(bundleIdentifier) && SdkPlatform != "WatchSimulator") { var text = File.ReadAllText(asset.ItemSpec); if (string.IsNullOrEmpty(text)) { continue; } var json = JsonConvert.DeserializeObject(text) as JObject; if (json == null) { continue; } var properties = json.Property("properties"); if (properties == null) { continue; } var resourceTags = properties.Value.ToObject <JObject> ().Property("on-demand-resource-tags"); if (resourceTags == null || resourceTags.Value.Type != JTokenType.Array) { continue; } var tagArray = resourceTags.Value.ToObject <JArray> (); var tags = new HashSet <string> (); string hash; foreach (var tag in tagArray.Select(token => token.ToObject <string> ())) { tags.Add(tag); } var tagList = tags.ToList(); tagList.Sort(); var path = AssetPackUtils.GetAssetPackDirectory(intermediate, bundleIdentifier, tagList, out hash); if (knownSpecs.Add(hash)) { var assetpack = new PDictionary(); var ptags = new PArray(); Directory.CreateDirectory(path); for (int i = 0; i < tags.Count; i++) { ptags.Add(new PString(tagList[i])); } assetpack.Add("bundle-id", new PString(string.Format("{0}.asset-pack-{1}", bundleIdentifier, hash))); assetpack.Add("bundle-path", new PString(Path.GetFullPath(path))); assetpack.Add("tags", ptags); specs.Add(assetpack); } } } if (catalogs.Count == 0) { // There are no (supported?) asset catalogs return(true); } partialAppManifest = new TaskItem(Path.Combine(intermediate, "partial-info.plist")); if (specs.Count > 0) { outputSpecs = Path.Combine(intermediate, "output-specifications.plist"); specs.Save(outputSpecs, true); } var output = new TaskItem(intermediateBundleDir); Directory.CreateDirectory(intermediateBundleDir); // Note: Compile() will set the PartialAppManifest property if it is used... if ((rc = Compile(catalogs.ToArray(), output, manifest)) != 0) { if (File.Exists(manifest.ItemSpec)) { try { var log = PDictionary.FromFile(manifest.ItemSpec); LogWarningsAndErrors(log, catalogs[0]); } catch (FormatException) { Log.LogError("actool exited with code {0}", rc); } File.Delete(manifest.ItemSpec); } return(false); } if (PartialAppManifest != null && !File.Exists(PartialAppManifest.GetMetadata("FullPath"))) { Log.LogError("Partial Info.plist file was not generated: {0}", PartialAppManifest.GetMetadata("FullPath")); } try { var manifestOutput = PDictionary.FromFile(manifest.ItemSpec); LogWarningsAndErrors(manifestOutput, catalogs[0]); bundleResources.AddRange(GetCompiledBundleResources(manifestOutput, intermediateBundleDir)); outputManifests.Add(manifest); } catch (Exception ex) { Log.LogError("Failed to load output manifest for {0} for the file {2}: {1}", ToolName, ex.Message, manifest.ItemSpec); } foreach (var assetpack in specs.OfType <PDictionary> ()) { var path = Path.Combine(assetpack.GetString("bundle-path").Value, "Info.plist"); var bundlePath = PathUtils.AbsoluteToRelative(intermediate, path); var outputPath = Path.Combine(OutputPath, bundlePath); var rpath = Path.Combine(intermediate, bundlePath); var dict = new PDictionary(); dict.SetCFBundleIdentifier(assetpack.GetString("bundle-id").Value); dict.Add("Tags", assetpack.GetArray("tags").Clone()); dict.Save(path, true, true); var item = new TaskItem(rpath); item.SetMetadata("LogicalName", bundlePath); item.SetMetadata("OutputPath", outputPath); item.SetMetadata("Optimize", "false"); bundleResources.Add(item); } BundleResources = bundleResources.ToArray(); OutputManifests = outputManifests.ToArray(); return(!Log.HasLoggedErrors); }
public override bool Execute() { var intermediate = Path.Combine (IntermediateOutputPath, ToolName); var intermediateBundleDir = Path.Combine (intermediate, "bundle"); var manifest = new TaskItem (Path.Combine (intermediate, "asset-manifest.plist")); var bundleResources = new List<ITaskItem> (); var outputManifests = new List<ITaskItem> (); var catalogs = new List<ITaskItem> (); var unique = new HashSet<string> (); string bundleIdentifier = null; var knownSpecs = new HashSet<string> (); var specs = new PArray (); int rc; Log.LogTaskName ("ACTool"); Log.LogTaskProperty ("AppManifest", AppManifest); Log.LogTaskProperty ("DeviceModel", DeviceModel); Log.LogTaskProperty ("DeviceOSVersion", DeviceOSVersion); Log.LogTaskProperty ("ImageAssets", ImageAssets); Log.LogTaskProperty ("IntermediateOutputPath", IntermediateOutputPath); Log.LogTaskProperty ("IsWatchApp", IsWatchApp); Log.LogTaskProperty ("OptimizePNGs", OptimizePNGs); Log.LogTaskProperty ("OutputPath", OutputPath); Log.LogTaskProperty ("ProjectDir", ProjectDir); Log.LogTaskProperty ("ResourcePrefix", ResourcePrefix); Log.LogTaskProperty ("SdkBinPath", SdkBinPath); Log.LogTaskProperty ("SdkPlatform", SdkPlatform); Log.LogTaskProperty ("SdkVersion", SdkVersion); switch (SdkPlatform) { case "iPhoneSimulator": case "iPhoneOS": case "MacOSX": case "WatchSimulator": case "WatchOS": case "AppleTVSimulator": case "AppleTVOS": break; default: Log.LogError ("Unrecognized platform: {0}", SdkPlatform); return false; } if (AppManifest != null) { try { plist = PDictionary.FromFile (AppManifest.ItemSpec); } catch (Exception ex) { Log.LogError (null, null, null, AppManifest.ItemSpec, 0, 0, 0, 0, "{0}", ex.Message); return false; } bundleIdentifier = plist.GetCFBundleIdentifier (); } foreach (var asset in ImageAssets) { var vpath = BundleResource.GetVirtualProjectPath (ProjectDir, asset); if (Path.GetFileName (vpath) != "Contents.json") continue; // get the parent (which will typically be .appiconset, .launchimage, .imageset, .iconset, etc) var catalog = Path.GetDirectoryName (vpath); // keep walking up the directory structure until we get to the .xcassets directory while (!string.IsNullOrEmpty (catalog) && Path.GetExtension (catalog) != ".xcassets") catalog = Path.GetDirectoryName (catalog); if (string.IsNullOrEmpty (catalog)) { Log.LogWarning (null, null, null, asset.ItemSpec, 0, 0, 0, 0, "Asset not part of an asset catalog: {0}", asset.ItemSpec); continue; } if (unique.Add (catalog)) catalogs.Add (new TaskItem (catalog)); if (AppleSdkSettings.XcodeVersion.Major >= 7 && !string.IsNullOrEmpty (bundleIdentifier) && SdkPlatform != "WatchSimulator") { var text = File.ReadAllText (asset.ItemSpec); if (string.IsNullOrEmpty (text)) continue; var json = JsonConvert.DeserializeObject (text) as JObject; if (json == null) continue; var properties = json.Property ("properties"); if (properties == null) continue; var resourceTags = properties.Value.ToObject<JObject> ().Property ("on-demand-resource-tags"); if (resourceTags == null || resourceTags.Value.Type != JTokenType.Array) continue; var tagArray = resourceTags.Value.ToObject<JArray> (); var tags = new HashSet<string> (); string hash; foreach (var tag in tagArray.Select (token => token.ToObject<string> ())) tags.Add (tag); var tagList = tags.ToList (); tagList.Sort (); var path = AssetPackUtils.GetAssetPackDirectory (intermediate, bundleIdentifier, tagList, out hash); if (knownSpecs.Add (hash)) { var assetpack = new PDictionary (); var ptags = new PArray (); Directory.CreateDirectory (path); for (int i = 0; i < tags.Count; i++) ptags.Add (new PString (tagList[i])); assetpack.Add ("bundle-id", new PString (string.Format ("{0}.asset-pack-{1}", bundleIdentifier, hash))); assetpack.Add ("bundle-path", new PString (Path.GetFullPath (path))); assetpack.Add ("tags", ptags); specs.Add (assetpack); } } } if (catalogs.Count == 0) { // There are no (supported?) asset catalogs return true; } partialAppManifest = new TaskItem (Path.Combine (intermediate, "partial-info.plist")); if (specs.Count > 0) { outputSpecs = Path.Combine (intermediate, "output-specifications.plist"); specs.Save (outputSpecs, true); } var output = new TaskItem (intermediateBundleDir); Directory.CreateDirectory (intermediateBundleDir); // Note: Compile() will set the PartialAppManifest property if it is used... if ((rc = Compile (catalogs.ToArray (), output, manifest)) != 0) { if (File.Exists (manifest.ItemSpec)) { try { var log = PDictionary.FromFile (manifest.ItemSpec); LogWarningsAndErrors (log, catalogs[0]); } catch (FormatException) { Log.LogError ("actool exited with code {0}", rc); } File.Delete (manifest.ItemSpec); } return false; } if (PartialAppManifest != null && !File.Exists (PartialAppManifest.GetMetadata ("FullPath"))) Log.LogError ("Partial Info.plist file was not generated: {0}", PartialAppManifest.GetMetadata ("FullPath")); try { var manifestOutput = PDictionary.FromFile (manifest.ItemSpec); LogWarningsAndErrors (manifestOutput, catalogs[0]); bundleResources.AddRange (GetCompiledBundleResources (manifestOutput, intermediateBundleDir)); outputManifests.Add (manifest); } catch (Exception ex) { Log.LogError ("Failed to load output manifest for {0} for the file {2}: {1}", ToolName, ex.Message, manifest.ItemSpec); } foreach (var assetpack in specs.OfType<PDictionary> ()) { var path = Path.Combine (assetpack.GetString ("bundle-path").Value, "Info.plist"); var bundlePath = PathUtils.AbsoluteToRelative (intermediate, path); var outputPath = Path.Combine (OutputPath, bundlePath); var rpath = Path.Combine (intermediate, bundlePath); var dict = new PDictionary (); dict.SetCFBundleIdentifier (assetpack.GetString ("bundle-id").Value); dict.Add ("Tags", assetpack.GetArray ("tags").Clone ()); dict.Save (path, true, true); var item = new TaskItem (rpath); item.SetMetadata ("LogicalName", bundlePath); item.SetMetadata ("OutputPath", outputPath); item.SetMetadata ("Optimize", "false"); bundleResources.Add (item); } BundleResources = bundleResources.ToArray (); OutputManifests = outputManifests.ToArray (); return !Log.HasLoggedErrors; }