public override bool Execute()
        {
            DateTime startTime = DateTime.Now;

            Log.LogMessage(MessageImportance.High, "Writing package usage data...");

            string[] projectDirectoriesOutsideRoot = ProjectDirectories.NullAsEmpty()
                                                     .Where(dir => !dir.StartsWith(RootDir, StringComparison.Ordinal))
                                                     .ToArray();

            if (projectDirectoriesOutsideRoot.Any())
            {
                throw new ArgumentException(
                          $"All ProjectDirectories must be in RootDir '{RootDir}', but found " +
                          string.Join(", ", projectDirectoriesOutsideRoot));
            }

            Log.LogMessage(MessageImportance.Low, "Finding set of RIDs...");

            string[] possibleRids = PlatformsRuntimeJsonFiles.NullAsEmpty()
                                    .SelectMany(ReadRidsFromRuntimeJson)
                                    .Distinct()
                                    .ToArray();

            Log.LogMessage(MessageImportance.Low, "Reading package identities...");

            PackageIdentity[] restored = RestoredPackageFiles.NullAsEmpty()
                                         .Select(ReadNuGetPackageInfos.ReadIdentity)
                                         .Distinct()
                                         .ToArray();

            PackageIdentity[] tarballPrebuilt = TarballPrebuiltPackageFiles.NullAsEmpty()
                                                .Select(ReadNuGetPackageInfos.ReadIdentity)
                                                .Distinct()
                                                .ToArray();

            PackageIdentity[] referencePackages = ReferencePackageFiles.NullAsEmpty()
                                                  .Select(ReadNuGetPackageInfos.ReadIdentity)
                                                  .Distinct()
                                                  .ToArray();

            PackageIdentity[] sourceBuilt = SourceBuiltPackageFiles.NullAsEmpty()
                                            .Select(ReadNuGetPackageInfos.ReadIdentity)
                                            .Distinct()
                                            .ToArray();

            IEnumerable <PackageIdentity> prebuilt = restored.Except(sourceBuilt).Except(referencePackages);

            PackageIdentity[] toCheck = NuGetPackageInfos.NullAsEmpty()
                                        .Select(item => new PackageIdentity(
                                                    item.GetMetadata("PackageId"),
                                                    NuGetVersion.Parse(item.GetMetadata("PackageVersion"))))
                                        .Concat(prebuilt)
                                        .ToArray();

            Log.LogMessage(MessageImportance.Low, "Finding project.assets.json files...");

            string[] assetFiles = Directory
                                  .GetFiles(RootDir, "project.assets.json", SearchOption.AllDirectories)
                                  .Select(GetPathRelativeToRoot)
                                  .Except(IgnoredProjectAssetsJsonFiles.NullAsEmpty().Select(GetPathRelativeToRoot))
                                  .ToArray();

            if (!string.IsNullOrEmpty(ProjectAssetsJsonArchiveFile))
            {
                Log.LogMessage(MessageImportance.Low, "Archiving project.assets.json files...");

                Directory.CreateDirectory(Path.GetDirectoryName(ProjectAssetsJsonArchiveFile));

                using (var projectAssetArchive = new ZipArchive(
                           File.Open(
                               ProjectAssetsJsonArchiveFile,
                               FileMode.Create,
                               FileAccess.ReadWrite),
                           ZipArchiveMode.Create))
                {
                    // Only one entry can be open at a time, so don't do this during the Parallel
                    // ForEach later.
                    foreach (var relativePath in assetFiles)
                    {
                        using (var stream = File.OpenRead(Path.Combine(RootDir, relativePath)))
                            using (Stream entryWriter = projectAssetArchive
                                                        .CreateEntry(relativePath, CompressionLevel.Optimal)
                                                        .Open())
                            {
                                stream.CopyTo(entryWriter);
                            }
                    }
                }
            }

            Log.LogMessage(MessageImportance.Low, "Reading usage info...");

            var usages = new ConcurrentBag <Usage>();

            Parallel.ForEach(
                assetFiles,
                assetFile =>
            {
                JObject jObj;

                using (var file = File.OpenRead(Path.Combine(RootDir, assetFile)))
                    using (var reader = new StreamReader(file))
                        using (var jsonReader = new JsonTextReader(reader))
                        {
                            jObj = (JObject)JToken.ReadFrom(jsonReader);
                        }

                var properties = new HashSet <string>(
                    jObj.SelectTokens("$.targets.*").Children()
                    .Concat(jObj.SelectToken("$.libraries"))
                    .Select(t => ((JProperty)t).Name)
                    .Distinct(),
                    StringComparer.OrdinalIgnoreCase);

                var directDependencies = jObj.SelectTokens("$.project.frameworks.*.dependencies").Children().Select(dep =>
                                                                                                                    new
                {
                    name           = ((JProperty)dep).Name,
                    target         = dep.SelectToken("$..target")?.ToString(),
                    version        = VersionRange.Parse(dep.SelectToken("$..version")?.ToString()),
                    autoReferenced = dep.SelectToken("$..autoReferenced")?.ToString() == "True",
                })
                                         .ToArray();

                foreach (var identity in toCheck
                         .Where(id => properties.Contains(id.Id + "/" + id.Version.OriginalVersion)))
                {
                    var directDependency =
                        directDependencies?.FirstOrDefault(
                            d => d.name == identity.Id &&
                            d.version.Satisfies(identity.Version));
                    usages.Add(Usage.Create(
                                   assetFile,
                                   identity,
                                   directDependency != null,
                                   directDependency?.autoReferenced == true,
                                   possibleRids));
                }
            });

            Log.LogMessage(MessageImportance.Low, "Searching for unused packages...");

            foreach (PackageIdentity restoredWithoutUsagesFound in
                     toCheck.Except(usages.Select(u => u.PackageIdentity)))
            {
                usages.Add(Usage.Create(
                               null,
                               restoredWithoutUsagesFound,
                               false,
                               false,
                               possibleRids));
            }

            // Packages that were included in the tarball as prebuilts, but weren't even restored.
            PackageIdentity[] neverRestoredTarballPrebuilts = tarballPrebuilt
                                                              .Except(restored)
                                                              .ToArray();

            Log.LogMessage(MessageImportance.Low, $"Writing data to '{DataFile}'...");

            var data = new UsageData
            {
                CreatedByRid = TargetRid,
                Usages       = usages.ToArray(),
                NeverRestoredTarballPrebuilts = neverRestoredTarballPrebuilts,
                ProjectDirectories            = ProjectDirectories
                                                ?.Select(GetPathRelativeToRoot)
                                                .ToArray()
            };

            Directory.CreateDirectory(Path.GetDirectoryName(DataFile));
            File.WriteAllText(DataFile, data.ToXml().ToString());

            Log.LogMessage(
                MessageImportance.High,
                $"Writing package usage data... done. Took {DateTime.Now - startTime}");

            return(!Log.HasLoggedErrors);
        }
        public override bool Execute()
        {
            DateTime startTime = DateTime.Now;

            Log.LogMessage(MessageImportance.High, "Writing package usage data...");

            string[] projectDirectoriesOutsideRoot = ProjectDirectories.NullAsEmpty()
                                                     .Where(dir => !dir.StartsWith(RootDir, StringComparison.Ordinal))
                                                     .ToArray();

            if (projectDirectoriesOutsideRoot.Any())
            {
                throw new ArgumentException(
                          $"All ProjectDirectories must be in RootDir '{RootDir}', but found " +
                          string.Join(", ", projectDirectoriesOutsideRoot));
            }

            Log.LogMessage(MessageImportance.Low, "Finding set of RIDs...");

            string[] possibleRids = PlatformsRuntimeJsonFiles.NullAsEmpty()
                                    .SelectMany(ReadRidsFromRuntimeJson)
                                    .Distinct()
                                    .ToArray();

            Log.LogMessage(MessageImportance.Low, "Reading package identities...");

            PackageIdentity[] restored = RestoredPackageFiles.NullAsEmpty()
                                         .Select(ReadNuGetPackageInfos.ReadIdentity)
                                         .Distinct()
                                         .ToArray();

            PackageIdentity[] tarballPrebuilt = TarballPrebuiltPackageFiles.NullAsEmpty()
                                                .Select(ReadNuGetPackageInfos.ReadIdentity)
                                                .Distinct()
                                                .ToArray();

            PackageIdentity[] sourceBuilt = SourceBuiltPackageFiles.NullAsEmpty()
                                            .Select(ReadNuGetPackageInfos.ReadIdentity)
                                            .Distinct()
                                            .ToArray();

            IEnumerable <PackageIdentity> prebuilt = restored.Except(sourceBuilt);

            PackageIdentity[] toCheck = NuGetPackageInfos.NullAsEmpty()
                                        .Select(item => new PackageIdentity(
                                                    item.GetMetadata("PackageId"),
                                                    NuGetVersion.Parse(item.GetMetadata("PackageVersion"))))
                                        .Concat(prebuilt)
                                        .ToArray();

            Log.LogMessage(MessageImportance.Low, "Finding project.assets.json files...");

            string[] assetFiles = Directory.GetFiles(
                RootDir,
                "project.assets.json",
                SearchOption.AllDirectories);

            if (!string.IsNullOrEmpty(ProjectAssetsJsonArchiveFile))
            {
                Log.LogMessage(MessageImportance.Low, "Archiving project.assets.json files...");

                Directory.CreateDirectory(Path.GetDirectoryName(ProjectAssetsJsonArchiveFile));

                using (var projectAssetArchive = new ZipArchive(
                           File.Open(
                               ProjectAssetsJsonArchiveFile,
                               FileMode.Create,
                               FileAccess.ReadWrite),
                           ZipArchiveMode.Create))
                {
                    // Only one entry can be open at a time, so don't do this during the Parallel
                    // ForEach later.
                    foreach (var file in assetFiles)
                    {
                        string relativePath = file.Substring(RootDir.Length);
                        using (var stream = File.OpenRead(file))
                            using (Stream entryWriter = projectAssetArchive
                                                        .CreateEntry(relativePath, CompressionLevel.Optimal)
                                                        .Open())
                            {
                                stream.CopyTo(entryWriter);
                            }
                    }
                }
            }

            Log.LogMessage(MessageImportance.Low, "Reading usage info...");

            var usages = new ConcurrentBag <Usage>();

            Parallel.ForEach(
                assetFiles,
                assetFile =>
            {
                var properties = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

                using (var file = File.OpenRead(assetFile))
                    using (var reader = new StreamReader(file))
                        using (var jsonReader = new JsonTextReader(reader))
                        {
                            while (jsonReader.Read())
                            {
                                if (jsonReader.TokenType == JsonToken.PropertyName &&
                                    jsonReader.Value is string value)
                                {
                                    properties.Add(value);
                                }
                            }
                        }

                foreach (var identity in toCheck
                         .Where(id => properties.Contains(id.Id + "/" + id.Version.OriginalVersion)))
                {
                    usages.Add(Usage.Create(
                                   // Store relative path for future report generation.
                                   assetFile.Substring(RootDir.Length),
                                   identity,
                                   possibleRids));
                }
            });

            Log.LogMessage(MessageImportance.Low, "Searching for unused packages...");

            foreach (PackageIdentity restoredWithoutUsagesFound in
                     toCheck.Except(usages.Select(u => u.PackageIdentity)))
            {
                usages.Add(Usage.Create(
                               null,
                               restoredWithoutUsagesFound,
                               possibleRids));
            }

            // Packages that were included in the tarball as prebuilts, but weren't even restored.
            PackageIdentity[] neverRestoredTarballPrebuilts = tarballPrebuilt
                                                              .Except(restored)
                                                              .ToArray();

            Log.LogMessage(MessageImportance.Low, $"Writing data to '{DataFile}'...");

            var data = new UsageData
            {
                CreatedByRid = TargetRid,
                Usages       = usages.ToArray(),
                NeverRestoredTarballPrebuilts = neverRestoredTarballPrebuilts,
                ProjectDirectories            = ProjectDirectories
                                                ?.Select(dir => dir.Substring(RootDir.Length))
                                                .ToArray()
            };

            Directory.CreateDirectory(Path.GetDirectoryName(DataFile));
            File.WriteAllText(DataFile, data.ToXml().ToString());

            Log.LogMessage(
                MessageImportance.High,
                $"Writing package usage data... done. Took {DateTime.Now - startTime}");

            return(!Log.HasLoggedErrors);
        }