269 строки
9.3 KiB
C#
269 строки
9.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
using Microsoft.Build.Framework;
|
|
using Microsoft.Build.Utilities;
|
|
|
|
using Xamarin.MacDev;
|
|
using Xamarin.MacDev.Tasks;
|
|
using Xamarin.Utils;
|
|
|
|
namespace Xamarin.iOS.Tasks
|
|
{
|
|
public abstract class ArchiveTaskBase : Xamarin.MacDev.Tasks.ArchiveTaskBase
|
|
{
|
|
[Required]
|
|
public ITaskItem[] ITunesSourceFiles { get; set; }
|
|
|
|
public ITaskItem[] AppExtensionReferences { get; set; }
|
|
|
|
public ITaskItem[] WatchAppReferences { get; set; }
|
|
|
|
static bool IsWatchAppExtension (ITaskItem appex, PDictionary plist, out string watchAppBundleDir)
|
|
{
|
|
PString expectedBundleIdentifier, bundleIdentifier, extensionPoint;
|
|
PDictionary extension, attributes;
|
|
|
|
watchAppBundleDir = null;
|
|
|
|
if (!plist.TryGetValue ("NSExtension", out extension))
|
|
return false;
|
|
|
|
if (!extension.TryGetValue ("NSExtensionPointIdentifier", out extensionPoint))
|
|
return false;
|
|
|
|
if (extensionPoint.Value != "com.apple.watchkit")
|
|
return false;
|
|
|
|
// Okay, we've found the WatchKit App Extension...
|
|
if (!extension.TryGetValue ("NSExtensionAttributes", out attributes))
|
|
return false;
|
|
|
|
if (!attributes.TryGetValue ("WKAppBundleIdentifier", out expectedBundleIdentifier))
|
|
return false;
|
|
|
|
var pwd = PathUtils.ResolveSymbolicLinks (Environment.CurrentDirectory);
|
|
|
|
// Scan the *.app subdirectories to find the WatchApp bundle...
|
|
foreach (var bundle in Directory.GetDirectories (appex.ItemSpec, "*.app")) {
|
|
if (!File.Exists (Path.Combine (bundle, "Info.plist")))
|
|
continue;
|
|
|
|
plist = PDictionary.FromFile (Path.Combine (bundle, "Info.plist"));
|
|
|
|
if (!plist.TryGetValue ("CFBundleIdentifier", out bundleIdentifier))
|
|
continue;
|
|
|
|
if (bundleIdentifier.Value != expectedBundleIdentifier.Value)
|
|
continue;
|
|
|
|
watchAppBundleDir = PathUtils.AbsoluteToRelative (pwd, PathUtils.ResolveSymbolicLinks (bundle));
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ArchiveAppExtension (ITaskItem appex, string archiveDir)
|
|
{
|
|
var plist = PDictionary.FromFile (Path.Combine (appex.ItemSpec, "Info.plist"));
|
|
string watchAppBundleDir;
|
|
|
|
if (IsWatchAppExtension (appex, plist, out watchAppBundleDir)) {
|
|
var wk = Path.Combine (watchAppBundleDir, "_WatchKitStub", "WK");
|
|
var supportDir = Path.Combine (archiveDir, "WatchKitSupport");
|
|
|
|
if (File.Exists (wk) && !Directory.Exists (supportDir)) {
|
|
Directory.CreateDirectory (supportDir);
|
|
File.Copy (wk, Path.Combine (supportDir, "WK"), true);
|
|
}
|
|
}
|
|
|
|
// Note: App Extension dSYM dirs exist alongside the main app bundle now that they are generated from the main app's MSBuild targets
|
|
var dsymDir = Path.Combine (Path.GetDirectoryName (AppBundleDir.ItemSpec), Path.GetFileName (appex.ItemSpec) + ".dSYM");
|
|
ArchiveDSym (dsymDir, archiveDir);
|
|
|
|
var msymDir = appex.ItemSpec + ".mSYM";
|
|
ArchiveMSym (msymDir, archiveDir);
|
|
}
|
|
|
|
void ArchiveWatchApp (ITaskItem watchApp, string archiveDir)
|
|
{
|
|
var wk = Path.Combine (watchApp.ItemSpec, "_WatchKitStub", "WK");
|
|
var supportDir = Path.Combine (archiveDir, "WatchKitSupport2");
|
|
|
|
if (File.Exists (wk) && !Directory.Exists (supportDir)) {
|
|
Directory.CreateDirectory (supportDir);
|
|
File.Copy (wk, Path.Combine (supportDir, "WK"), true);
|
|
}
|
|
|
|
var dsymDir = watchApp.ItemSpec + ".dSYM";
|
|
ArchiveDSym (dsymDir, archiveDir);
|
|
|
|
var msymDir = watchApp.ItemSpec + ".mSYM";
|
|
ArchiveMSym (msymDir, archiveDir);
|
|
}
|
|
|
|
void AddIconPaths (PArray icons, PArray iconFiles, string productsDir)
|
|
{
|
|
foreach (var icon in iconFiles.Cast<PString> ().Where (p => p.Value != null)) {
|
|
var path = string.Format ("Applications/{0}/{1}", Path.GetFileName (AppBundleDir.ItemSpec), icon.Value);
|
|
bool addDefault = true;
|
|
|
|
if (path.EndsWith (".png", StringComparison.Ordinal)) {
|
|
icons.Add (new PString (path));
|
|
continue;
|
|
}
|
|
|
|
if (File.Exists (Path.Combine (productsDir, path + "@3x.png"))) {
|
|
icons.Add (new PString (path + "@3x.png"));
|
|
addDefault = false;
|
|
}
|
|
|
|
if (File.Exists (Path.Combine (productsDir, path + "@2x.png"))) {
|
|
icons.Add (new PString (path + "@2x.png"));
|
|
addDefault = false;
|
|
}
|
|
|
|
if (addDefault || File.Exists (Path.Combine (productsDir, path + ".png")))
|
|
icons.Add (new PString (path + ".png"));
|
|
}
|
|
}
|
|
|
|
public override bool Execute ()
|
|
{
|
|
var archiveDir = CreateArchiveDirectory ();
|
|
|
|
try {
|
|
var plist = PDictionary.FromFile (Path.Combine (AppBundleDir.ItemSpec, "Info.plist"));
|
|
var productsDir = Path.Combine (archiveDir, "Products");
|
|
|
|
// Archive the OnDemandResources...
|
|
var resourcesDestDir = Path.Combine (productsDir, "OnDemandResources");
|
|
var resourcesSrcDir = Path.Combine (OutputPath, "OnDemandResources");
|
|
|
|
if (Directory.Exists (resourcesSrcDir))
|
|
Ditto (resourcesSrcDir, resourcesDestDir);
|
|
|
|
// Archive the Applications...
|
|
var appDestDir = Path.Combine (productsDir, "Applications", Path.GetFileName (AppBundleDir.ItemSpec));
|
|
Ditto (AppBundleDir.ItemSpec, appDestDir);
|
|
|
|
// Archive the dSYMs...
|
|
ArchiveDSym (DSYMDir, archiveDir);
|
|
|
|
// for each user framework that is bundled inside the app we must also archive their dSYMs, if available
|
|
var fwks = Path.Combine (AppBundleDir.ItemSpec, "Frameworks");
|
|
if (Directory.Exists (fwks)) {
|
|
foreach (var fx in Directory.GetDirectories (fwks, "*.framework")) {
|
|
var dsym = Path.GetFileName (fx) + ".dSYM";
|
|
var fq_dsym = Path.Combine (AppBundleDir.ItemSpec, "..", dsym);
|
|
ArchiveDSym (fq_dsym, archiveDir);
|
|
}
|
|
}
|
|
|
|
// Archive the mSYMs...
|
|
ArchiveMSym (MSYMDir, archiveDir);
|
|
|
|
// Archive the Bitcode symbol maps
|
|
var bcSymbolMaps = Directory.GetFiles (Path.GetDirectoryName (DSYMDir), "*.bcsymbolmap");
|
|
if (bcSymbolMaps.Length > 0) {
|
|
var bcSymbolMapsDir = Path.Combine (archiveDir, "BCSymbolMaps");
|
|
|
|
Directory.CreateDirectory (bcSymbolMapsDir);
|
|
|
|
for (int i = 0; i < bcSymbolMaps.Length; i++)
|
|
File.Copy (bcSymbolMaps[i], Path.Combine (bcSymbolMapsDir, Path.GetFileName (bcSymbolMaps[i])));
|
|
}
|
|
|
|
if (AppExtensionReferences != null) {
|
|
// Archive the dSYMs, mSYMs, etc for each of the referenced App Extensions as well...
|
|
for (int i = 0; i < AppExtensionReferences.Length; i++)
|
|
ArchiveAppExtension (AppExtensionReferences[i], archiveDir);
|
|
}
|
|
|
|
if (WatchAppReferences != null) {
|
|
// Archive the dSYMs, mSYMs, etc for each of the referenced WatchOS2 Apps as well...
|
|
for (int i = 0; i < WatchAppReferences.Length; i++)
|
|
ArchiveWatchApp (WatchAppReferences[i], archiveDir);
|
|
}
|
|
|
|
if (ITunesSourceFiles != null) {
|
|
// Archive the iTunesMetadata.plist and iTunesArtwork files...
|
|
var iTunesMetadataDir = Path.Combine (archiveDir, "iTunesMetadata", Path.GetFileName (AppBundleDir.ItemSpec));
|
|
for (int i = 0; i < ITunesSourceFiles.Length; i++) {
|
|
var archivedMetaFile = Path.Combine (iTunesMetadataDir, Path.GetFileName (ITunesSourceFiles[i].ItemSpec));
|
|
|
|
Directory.CreateDirectory (iTunesMetadataDir);
|
|
File.Copy (ITunesSourceFiles[i].ItemSpec, archivedMetaFile, true);
|
|
}
|
|
}
|
|
|
|
// Generate an archive Info.plist
|
|
var arInfo = new PDictionary ();
|
|
// FIXME: figure out this value
|
|
//arInfo.Add ("AppStoreFileSize", new PNumber (65535));
|
|
|
|
var props = new PDictionary ();
|
|
props.Add ("ApplicationPath", new PString (string.Format ("Applications/{0}", Path.GetFileName (AppBundleDir.ItemSpec))));
|
|
props.Add ("CFBundleIdentifier", new PString (plist.GetCFBundleIdentifier ()));
|
|
|
|
var version = plist.GetCFBundleShortVersionString ();
|
|
var build = plist.GetCFBundleVersion ();
|
|
|
|
props.Add ("CFBundleShortVersionString", new PString (version ?? (build ?? "1.0")));
|
|
props.Add ("CFBundleVersion", new PString (build ?? "1.0"));
|
|
|
|
var iconFiles = plist.GetCFBundleIconFiles ();
|
|
var iconDict = plist.GetCFBundleIcons ();
|
|
var icons = new PArray ();
|
|
|
|
if (iconFiles != null)
|
|
AddIconPaths (icons, iconFiles, Path.Combine (archiveDir, "Products"));
|
|
|
|
if (iconDict != null) {
|
|
var primary = iconDict.Get<PDictionary> (ManifestKeys.CFBundlePrimaryIcon);
|
|
if (primary != null && (iconFiles = primary.GetCFBundleIconFiles ()) != null)
|
|
AddIconPaths (icons, iconFiles, Path.Combine (archiveDir, "Products"));
|
|
}
|
|
|
|
if (icons.Count > 0)
|
|
props.Add ("IconPaths", icons);
|
|
|
|
props.Add ("SigningIdentity", new PString (SigningKey));
|
|
|
|
arInfo.Add ("ApplicationProperties", props);
|
|
arInfo.Add ("ArchiveVersion", new PNumber (2));
|
|
arInfo.Add ("CreationDate", new PDate (Now.ToUniversalTime ()));
|
|
arInfo.Add ("Name", new PString (plist.GetCFBundleName () ?? plist.GetCFBundleDisplayName ()));
|
|
arInfo.Add ("SchemeName", new PString (ProjectName));
|
|
|
|
if (!string.IsNullOrEmpty (ProjectGuid))
|
|
arInfo.Add ("ProjectGuid", new PString (ProjectGuid));
|
|
|
|
if (!string.IsNullOrEmpty (ProjectTypeGuids))
|
|
arInfo.Add ("ProjectTypeGuids", new PString (ProjectTypeGuids));
|
|
|
|
if (!string.IsNullOrEmpty (SolutionPath)) {
|
|
arInfo.Add ("SolutionName", new PString (Path.GetFileNameWithoutExtension (SolutionPath)));
|
|
arInfo.Add ("SolutionPath", new PString (SolutionPath));
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty (InsightsApiKey))
|
|
arInfo.Add ("InsightsApiKey", new PString (InsightsApiKey));
|
|
|
|
arInfo.Save (Path.Combine (archiveDir, "Info.plist"));
|
|
|
|
ArchiveDir = archiveDir;
|
|
} catch (Exception ex) {
|
|
Log.LogErrorFromException (ex);
|
|
Directory.Delete (archiveDir, true);
|
|
}
|
|
|
|
return !Log.HasLoggedErrors;
|
|
}
|
|
}
|
|
}
|