public int Execute(Execution execution)
        {
            var archiveType = this.DetectPackageType(execution.PackagePushFile);

            if (archiveType == PackageManager.ARCHIVE_FORMAT_NUGET_ZIP)
            {
                if (execution.PackagePushVersion != ArgumentOmitted || execution.PackagePushPlatform != ArgumentOmitted)
                {
                    RedirectableConsole.ErrorWriteLine("You must omit the version and platform arguments when pushing packages in the NuGet format.");
                }
            }
            else
            {
                if (execution.PackagePushVersion == ArgumentOmitted || execution.PackagePushPlatform == ArgumentOmitted)
                {
                    RedirectableConsole.ErrorWriteLine("You must provide the version and platform arguments.");
                }
            }

            RedirectableConsole.WriteLine("Detected package type as " + archiveType + ".");

            switch (archiveType)
            {
            case PackageManager.ARCHIVE_FORMAT_NUGET_ZIP:
                return(PushToNuGetRepository(execution));

            case PackageManager.ARCHIVE_FORMAT_TAR_GZIP:
            case PackageManager.ARCHIVE_FORMAT_TAR_LZMA:
            default:
                return(PushToProtobuildRepository(execution, archiveType));
            }
        }
Beispiel #2
0
        private static string FindGitOnSystemPath()
        {
            if (Path.DirectorySeparatorChar != '/')
            {
                // We're on Windows.  We split the environment PATH to see if we
                // can find Git, then we check standard directories (like
                // C:\Program Files (x86)\Git) etc.
                var pathEnv = Environment.GetEnvironmentVariable("PATH");
                var paths   = new string[0];
                if (pathEnv != null)
                {
                    paths = pathEnv.Split(';');
                }

                var standardPaths = new List <string>
                {
                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Git", "cmd"),
                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Git", "bin"),
                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Git", "cmd"),
                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Git", "bin"),
                };

                // Add standard paths that GitHub for Windows uses.  Because the file
                // contains a hash, or some other mutable component, we need to search for
                // the PortableGit path.
                var github = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                                          "GitHub");
                if (Directory.Exists(github))
                {
                    foreach (var subfolder in new DirectoryInfo(github).GetDirectories())
                    {
                        if (subfolder.Name.StartsWith("PortableGit_"))
                        {
                            standardPaths.Add(Path.Combine(subfolder.FullName, "cmd"));
                        }
                    }
                }

                var filenames = new[] { "git.exe", "git.bat", "git.cmd" };
                foreach (var path in paths.Concat(standardPaths))
                {
                    foreach (var filename in filenames)
                    {
                        if (File.Exists(Path.Combine(path, filename)))
                        {
                            // We found Git.
                            return(Path.Combine(path, filename));
                        }
                    }
                }

                RedirectableConsole.ErrorWriteLine(
                    "WARNING: Unable to find Git on your PATH, or any standard " +
                    "locations.  Have you installed Git on this system?");
                return("git");
            }

            // For UNIX systems, Git should always be on the PATH.
            return("git");
        }
        public int Execute(Execution execution)
        {
            var scriptPath = execution.AutomatedBuildScriptPath ?? "automated.build";

            if (!File.Exists(scriptPath))
            {
                RedirectableConsole.ErrorWriteLine("ERROR: Automated build script not found at " + scriptPath + ".");
            }

            return(_automatedBuildController.Execute(execution.WorkingDirectory, scriptPath));
        }
Beispiel #4
0
        private static bool SSLValidationForLinux(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors)
        {
            // If the Mono root CA store is initialized properly, then use that.
            if (sslpolicyerrors == SslPolicyErrors.None)
            {
                // Good certificate.
                return(true);
            }

            // I'm not sure of the stability of the certificate thumbprint for protobuild.org
            // itself, so instead just verify that it is Let's Encrypt that's providing the
            // certificate.
            if (sender is HttpWebRequest &&
                (sender as HttpWebRequest).Host == "protobuild.org")
            {
                if (chain.ChainElements.Count > 2 &&
                    chain.ChainElements[1].Certificate.Thumbprint == "3EAE91937EC85D74483FF4B77B07B43E2AF36BF4")
                {
                    // This is the Let's Encrypt certificate authority.  We can implicitly trust this without warning.
                    return(true);
                }
                else
                {
                    // The thumbprint differs!  Show a danger message to the user, but continue anyway
                    // because if the thumbprint does legitimately change, we have no way of backporting
                    // a new certificate thumbprint without issuing a new version of Protobuild.
                    var givenThumbprint = chain.ChainElements.Count >= 2 ?
                                          chain.ChainElements[1].Certificate.Thumbprint :
                                          "<no thumbprint available>";
                    RedirectableConsole.ErrorWriteLine(
                        "DANGER: The thumbprint of the issuer's SSL certificate for protobuild.org \"" +
                        givenThumbprint + "\" does not match the expected thumbprint value \"" +
                        chain.ChainElements[1].Certificate.Thumbprint +
                        "\".  It's possible that Let's Encrypt " +
                        "changed their certificate thumbprint, or someone is performing a MITM " +
                        "attack on your connection.  Unfortunately Mono does not ship out-of-the-box " +
                        "with appropriate root CA certificates on Linux, so we have no method of verifying " +
                        "that the proposed thumbprint is correct.  You should verify that the given " +
                        "thumbprint is correct either through your web browser (by visiting protobuild.org " +
                        "and checking the certificate chain), or by performing the same operation on " +
                        "Mac OS or Windows.  If the operation succeeds, or the thumbprint matches, please " +
                        "file an issue at https://github.com/hach-que/Protobuild/issues/new so we can " +
                        "update the embedded thumbprint.  We will now continue the operation regardless " +
                        "as we can't update the thumbprint in previous versions if it has changed.");
                    return(true);
                }
            }

            RedirectableConsole.WriteLine(
                "WARNING: Implicitly trusting SSL certificate " + certificate.GetCertHashString() + " " +
                "for " + certificate.Subject + " issued by " + certificate.Issuer + " on Linux, due " +
                "to inconsistent root CA store policies of Mono.");
            return(true);
        }
        private void ShowSupportedPlatformsError(ModuleInfo module, string requestedPlatform)
        {
            RedirectableConsole.ErrorWriteLine("The platform '" + requestedPlatform + "' is not supported.");
            RedirectableConsole.ErrorWriteLine("The following platforms are supported by this module:");
            foreach (
                var supportedPlatform in
                module.SupportedPlatforms.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(x => x.Trim())
                .Where(x => !string.IsNullOrWhiteSpace(x)))
            {
                RedirectableConsole.ErrorWriteLine("  * " + supportedPlatform);
            }

            ExecEnvironment.Exit(1);
        }
 private void InstallAssemblyIntoGAC(string gacPath)
 {
     try
     {
         var assembly      = Assembly.Load("System.EnterpriseServices");
         var type          = assembly.GetType("System.EnterpriseServices.Internal.Publish");
         var constructor   = type.GetConstructor(Type.EmptyTypes);
         var publishObject = constructor.Invoke(null);
         var gacInstall    = type.GetMethod("GacInstall");
         gacInstall.Invoke(publishObject, new object[] { gacPath });
         RedirectableConsole.WriteLine("GAC installation completed successfully for '" + gacPath + "'");
     }
     catch (Exception ex)
     {
         RedirectableConsole.ErrorWriteLine("Got an exception while performing GAC install for '" + gacPath + "': " + ex.Message);
     }
 }
Beispiel #7
0
        private Feature?LookupFeatureByID(string str)
        {
            foreach (var name in Enum.GetNames(typeof(Feature)))
            {
                var field             = typeof(Feature).GetField(name);
                var internalAttribute = field.GetCustomAttributes(typeof(FeatureInternalAttribute), false).FirstOrDefault() as FeatureInternalAttribute;
                if (internalAttribute != null)
                {
                    if (internalAttribute.InternalId == str)
                    {
                        return((Feature)field.GetValue(null));
                    }
                }
            }

            RedirectableConsole.ErrorWriteLine("WARNING: Unable to find feature based on ID '" + str + "'");
            return(null);
        }
Beispiel #8
0
        private void PerformRetryableRequest(string message, Uri baseUri, Action <Uri> func)
        {
            var exceptions = new List <Exception>();
            var backoff    = 100;

            for (var i = 0; i < MaxRequests; i++)
            {
                try
                {
                    RedirectableConsole.WriteLine("(" + (i + 1) + "/" + MaxRequests + ") " + message);
                    func(baseUri);
                    return;
                }
                catch (Exception ex)
                {
                    RedirectableConsole.ErrorWriteLine("Exception during web request: ");
                    RedirectableConsole.ErrorWriteLine(ex);
                    exceptions.Add(ex);

                    var webException = ex as WebException;
                    if (webException != null)
                    {
                        switch (webException.Status)
                        {
                        case WebExceptionStatus.NameResolutionFailure:
                            // This is a permanent failure.
                            i       = MaxRequests;
                            backoff = 0;
                            break;
                        }
                    }

                    RedirectableConsole.WriteLine("Backing off web requests for " + backoff + "ms...");
                    System.Threading.Thread.Sleep(backoff);
                    backoff *= 2;
                    if (backoff > 20000)
                    {
                        backoff = 20000;
                    }
                }
            }

            throw new AggregateException(exceptions);
        }
        public int Execute(string workingDirectory, string path)
        {
            IAutomatedBuildRuntime runtime = null;
            string script = null;

            using (var reader = new StreamReader(path))
            {
                script = reader.ReadToEnd();
                if (script.StartsWith("#version 1"))
                {
                    script  = script.Substring("#version 1".Length).TrimStart();
                    runtime = _automatedBuildRuntimeV1;
                }
                else
                {
                    throw new InvalidOperationException(
                              "Your automated build script must start with #version N, where " +
                              "N is the number indicating the script runtime version for " +
                              "automated builds.");
                }
            }

            object handle;

            try
            {
                handle = runtime.Parse(script);
            }
            catch (Exception ex)
            {
                RedirectableConsole.ErrorWriteLine("ERROR: " + ex.Message);
                return(1);
            }
            try
            {
                return(runtime.Execute(workingDirectory, handle));
            }
            catch (Exception ex)
            {
                RedirectableConsole.ErrorWriteLine("ERROR: " + ex.Message + Environment.NewLine + ex.StackTrace);
                return(1);
            }
        }
Beispiel #10
0
        private string GetSourcePackage(string workingDirectory, string url)
        {
            var sourcePath = Path.Combine(
                _packageCacheConfiguration.GetCacheDirectory(),
                this.GetPackageName(url));

            if (this.HasSourcePackage(url))
            {
                if (Directory.Exists(Path.Combine(sourcePath, "objects")) &&
                    File.Exists(Path.Combine(sourcePath, "config")))
                {
                    try
                    {
                        GitUtils.RunGitAbsolute(sourcePath, "fetch origin +refs/heads/*:refs/heads/*");
                    }
                    catch (InvalidOperationException)
                    {
                        // Ignore exceptions here in case the user is offline.
                    }

                    return(sourcePath);
                }
                else
                {
                    RedirectableConsole.ErrorWriteLine("WARNING: Source package cache is corrupt, removing and cloning again...");
                    try
                    {
                        PathUtils.AggressiveDirectoryDelete(sourcePath);
                    }
                    catch (Exception)
                    {
                        RedirectableConsole.ErrorWriteLine("WARNING: Unable to delete invalid source package from cache!");
                    }
                }
            }

            Directory.CreateDirectory(sourcePath);
            GitUtils.RunGit(workingDirectory, null, "clone --progress --bare " + url + " \"" + sourcePath + "\"");

            return(sourcePath);
        }
Beispiel #11
0
        public void ValidateEnabledFeatures()
        {
            var features            = GetAllEnabledFeatures().OrderByDescending(x => (int)x).ToArray();
            var nonInternalFeatures = GetAllNonInternalFeatures().OrderByDescending(x => (int)x).ToList();
            var missingFeatures     = new List <Feature>();

            foreach (var enabledFeature in features)
            {
                var idx = nonInternalFeatures.IndexOf(enabledFeature);
                if (idx == -1)
                {
                    continue;
                }

                for (var i = idx; i < nonInternalFeatures.Count; i++)
                {
                    if (!features.Contains(nonInternalFeatures[i]))
                    {
                        if (!missingFeatures.Contains(nonInternalFeatures[i]))
                        {
                            missingFeatures.Add(nonInternalFeatures[i]);
                        }
                    }
                }
            }

            if (missingFeatures.Count > 0)
            {
                var featureList        = features.Length == 0 ? string.Empty : features.Select(x => x.ToString()).Aggregate((a, b) => a + "," + b);
                var missingFeatureList = missingFeatures.Count == 0 ? string.Empty : missingFeatures.Select(x => x.ToString()).Aggregate((a, b) => a + "," + b);

                RedirectableConsole.ErrorWriteLine(
                    "WARNING: The active feature set is missing previous features!  " +
                    "You have the following features enabled: '" +
                    featureList +
                    "', but the following features should also be enabled for stability reasons: '" +
                    missingFeatureList + "'");
            }
        }
Beispiel #12
0
        public int Execute(Execution execution)
        {
            var    hostPlatform          = _hostPlatformDetector.DetectPlatform();
            string builderPathNativeArch = null;
            string builderPath64         = null;
            string builderPath32         = null;
            var    extraArgsNativeArch   = string.Empty;
            var    extraArgs64           = string.Empty;
            var    extraArgs32           = string.Empty;
            var    extraArgsGeneral      = string.Empty;

            if (hostPlatform == "Windows")
            {
                foreach (var arch in new[] { RegistryView.Default, RegistryView.Registry32, RegistryView.Registry64 })
                {
                    // Find latest version of MSBuild.
                    var registryKey =
                        RegistryKey.OpenBaseKey(
                            RegistryHive.LocalMachine,
                            arch)
                        .OpenSubKey("SOFTWARE")?
                        .OpenSubKey("Microsoft")?
                        .OpenSubKey("MSBuild")?
                        .OpenSubKey("ToolsVersions");
                    if (registryKey == null)
                    {
                        if (arch == RegistryView.Registry64)
                        {
                            continue;
                        }

                        RedirectableConsole.ErrorWriteLine(
                            "ERROR: No versions of MSBuild were available " +
                            "according to the registry (or they were not readable).");
                        return(1);
                    }

                    var subkeys         = registryKey.GetSubKeyNames();
                    var orderedVersions =
                        subkeys.OrderByDescending(x => int.Parse(x.Split('.').First(), CultureInfo.InvariantCulture));
                    var builderPath = (from version in orderedVersions
                                       let path = (string)registryKey.OpenSubKey(version)?.GetValue("MSBuildToolsPath")
                                                  where path != null && Directory.Exists(path)
                                                  let msbuild = Path.Combine(path, "MSBuild.exe")
                                                                where File.Exists(msbuild)
                                                                select msbuild).FirstOrDefault();

                    if (builderPath == null)
                    {
                        if (arch == RegistryView.Registry64)
                        {
                            continue;
                        }

                        RedirectableConsole.ErrorWriteLine(
                            "ERROR: Unable to find installed MSBuild in any installed tools version.");
                        return(1);
                    }

                    var extraArgs = string.Empty;
                    if (!builderPath.Contains("v2.0.50727"))
                    {
                        extraArgs = "/m /nodeReuse:false ";
                    }

                    switch (arch)
                    {
                    case RegistryView.Default:
                        builderPathNativeArch = builderPath;
                        extraArgsNativeArch   = extraArgs;
                        break;

                    case RegistryView.Registry32:
                        builderPath32 = builderPath;
                        extraArgs32   = extraArgs;
                        break;

                    case RegistryView.Registry64:
                        builderPath64 = builderPath;
                        extraArgs64   = extraArgs;
                        break;
                    }
                }
            }
            else
            {
                // Find path to xbuild.
                var whichPaths = new[] { "/bin/which", "/usr/bin/which" };
                foreach (var w in whichPaths)
                {
                    if (File.Exists(w))
                    {
                        var whichProcess = Process.Start(new ProcessStartInfo(w, "xbuild")
                        {
                            RedirectStandardOutput = true,
                            UseShellExecute        = false
                        });
                        if (whichProcess == null)
                        {
                            continue;
                        }
                        var result = whichProcess.StandardOutput.ReadToEnd().Trim();
                        if (!string.IsNullOrWhiteSpace(result) && File.Exists(result))
                        {
                            builderPathNativeArch = result;
                            break;
                        }
                    }
                }

                if (builderPathNativeArch == null && _hostPlatformDetector.DetectPlatform() == "MacOS" && File.Exists("/usr/local/bin/xbuild"))
                {
                    // After upgrading to OSX El Capitan, the /usr/local/bin folder is no longer in
                    // the system PATH.  If we can't find xbuild with the which tool, manually set the
                    // path here in an attempt to find it.
                    builderPathNativeArch = "/usr/local/bin/xbuild";
                }

                if (builderPathNativeArch == null)
                {
                    RedirectableConsole.ErrorWriteLine("ERROR: Unable to find xbuild on the current PATH.");
                    return(1);
                }

                builderPath32 = builderPathNativeArch;
                builderPath64 = builderPathNativeArch;
            }

            if (!string.IsNullOrWhiteSpace(execution.BuildTarget))
            {
                extraArgsGeneral += "/t:\"" + execution.BuildTarget + "\" ";
            }
            foreach (var prop in execution.BuildProperties)
            {
                extraArgsGeneral += "/p:\"" + prop.Key.Replace("\"", "\\\"") + "\"=\"" + (prop.Value ?? string.Empty).Replace("\"", "\\\"") + "\" ";
            }

            switch (execution.BuildProcessArchitecture)
            {
            case "x86":
                RedirectableConsole.WriteLine("INFO: Using " + builderPath32 + " (forced 32-bit) to perform this build.");
                break;

            case "x64":
                RedirectableConsole.WriteLine("INFO: Using " + builderPath64 + " (forced 64-bit) to perform this build.");
                break;

            case "Default":
            default:
                RedirectableConsole.WriteLine("INFO: Using " + builderPathNativeArch + " (32-bit: " + builderPath32 + ") to perform this build.");
                break;
            }

            var targetPlatforms = (execution.Platform ?? hostPlatform).Split(',');
            var module          = ModuleInfo.Load(Path.Combine(execution.WorkingDirectory, "Build", "Module.xml"));

            foreach (var platform in targetPlatforms)
            {
                string builderPath;
                string extraArgs;

                switch (execution.BuildProcessArchitecture)
                {
                case "x86":
                    builderPath = builderPath32;
                    extraArgs   = extraArgs32 + extraArgsGeneral;
                    break;

                case "x64":
                    builderPath = builderPath64;
                    extraArgs   = extraArgs64 + extraArgsGeneral;
                    break;

                case "Default":
                default:
                    builderPath = platform == "WindowsPhone" ? builderPath32 : builderPathNativeArch;
                    extraArgs   = (platform == "WindowsPhone" ? extraArgs32 : extraArgsNativeArch) + extraArgsGeneral;
                    break;
                }

                var fileToBuild = module.Name + "." + platform + ".sln";

                RedirectableConsole.WriteLine("INFO: Executing " + builderPath + " with arguments: " + extraArgs + fileToBuild);

                var process =
                    Process.Start(new ProcessStartInfo(builderPath, extraArgs + fileToBuild)
                {
                    UseShellExecute = false
                });
                if (process == null)
                {
                    RedirectableConsole.ErrorWriteLine("ERROR: Build process did not start successfully.");
                    return(1);
                }
                process.WaitForExit();

                if (process.ExitCode != 0)
                {
                    return(process.ExitCode);
                }
            }

            return(0);
        }
Beispiel #13
0
        public byte[] Transform(string workingDirectory, string url, string gitReference, string platform, string format)
        {
            var urlAndPackageName = url.Split(new[] { '|' }, 2);

            if (urlAndPackageName.Length != 2)
            {
                RedirectableConsole.ErrorWriteLine(
                    "ERROR: Malformed NuGet package reference '" + url +
                    "'.  Make sure you split the NuGet server URL and the package name with a pipe character (|).");
                ExecEnvironment.Exit(1);
            }

            var repoUrl     = urlAndPackageName[0];
            var packageName = urlAndPackageName[1];

            var originalFolder = DownloadOrUseExistingNuGetPackage(repoUrl.TrimEnd('/'), packageName, gitReference);

            if (Directory.Exists(Path.Combine(originalFolder, "protobuild")))
            {
                // This is a Protobuild-aware NuGet package.  In this case, we just use the contents of the
                // "protobuild" folder as the content of our package, and ignore everything else.
                RedirectableConsole.WriteLine("Detected Protobuild-aware package...");

                RedirectableConsole.WriteLine("Converting to a Protobuild package...");

                var target = new MemoryStream();
                var filter = new FileFilter(_getRecursiveUtilitiesInPath.GetRecursiveFilesInPath(originalFolder));

                filter.ApplyInclude("protobuild/(.*)");
                filter.ApplyRewrite("protobuild/(.*)", "$1");

                filter.ImplyDirectories();

                _packageCreator.Create(
                    target,
                    filter,
                    originalFolder,
                    format,
                    platform);

                RedirectableConsole.WriteLine("Package conversion complete.");
                var bytes2 = new byte[target.Position];
                target.Seek(0, SeekOrigin.Begin);
                target.Read(bytes2, 0, bytes2.Length);
                return(bytes2);
            }

            var folder = Path.GetTempFileName();

            File.Delete(folder);
            Directory.CreateDirectory(folder);

            byte[] bytes;
            try
            {
                RedirectableConsole.WriteLine("Copying directory for package transformation...");
                CopyFolder(new DirectoryInfo(originalFolder), new DirectoryInfo(folder));

                RedirectableConsole.WriteLine("Auto-detecting libraries to reference from NuGet package...");

                var packagePath         = new DirectoryInfo(folder).GetFiles("*.nuspec").First().FullName;
                var libraryReferences   = new Dictionary <string, string>();
                var packageDependencies = new Dictionary <string, string>();

                // Use the nuspec file if it exists.
                List <string> references = new List <string>();
                if (File.Exists(packagePath))
                {
                    var packageDoc = new XmlDocument();
                    packageDoc.Load(packagePath);

                    // If the references are explicitly provided in the nuspec, use
                    // those as to what files should be referenced by the projects.
                    if (
                        packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>()
                        .Count(x => x.Name == "references") > 0)
                    {
                        references =
                            packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>()
                            .First(x => x.Name == "references")
                            .ChildNodes.OfType <XmlElement>()
                            .Where(x => x.Name == "reference")
                            .Select(x => x.Attributes["file"].Value)
                            .ToList();
                    }

                    // If there are dependencies specified, store them and convert them to
                    // Protobuild references, and reference them in the Module.xml file.
                    if (
                        packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>()
                        .Count(x => x.Name == "dependencies") > 0)
                    {
                        packageDependencies =
                            packageDoc.DocumentElement.FirstChild.ChildNodes.OfType <XmlElement>()
                            .First(x => x.Name == "dependencies")
                            .ChildNodes.OfType <XmlElement>()
                            .Where(x => x.Name == "dependency")
                            .ToDictionarySafe(
                                k => k.Attributes["id"].Value,
                                v => v.Attributes["version"].Value,
                                (dict, c) =>
                                RedirectableConsole.WriteLine("WARNING: More than one dependency on " + c +
                                                              " in NuGet package."));
                    }
                }

                // Determine the priority of the frameworks that we want to target
                // out of the available versions.
                string[] clrNames = _nuGetPlatformMapping.GetFrameworkNamesForRead(workingDirectory, platform);

                var referenceDirectories = new string[] { "ref", "lib" };

                foreach (var directory in referenceDirectories)
                {
                    // Determine the base path for all references; that is, the lib/ folder.
                    var referenceBasePath = Path.Combine(
                        folder,
                        directory);

                    if (Directory.Exists(referenceBasePath))
                    {
                        // If no references are in nuspec, reference all of the libraries that
                        // are on disk.
                        if (references.Count == 0)
                        {
                            // Search through all of the target frameworks until we find one that
                            // has at least one file in it.
                            foreach (var clrNameOriginal in clrNames)
                            {
                                var clrName  = clrNameOriginal;
                                var foundClr = false;

                                if (clrName[0] == '=')
                                {
                                    // Exact match (strip the equals).
                                    clrName = clrName.Substring(1);

                                    // If this target framework doesn't exist for this library, skip it.
                                    var dirPath = Path.Combine(
                                        referenceBasePath,
                                        clrName);
                                    if (!Directory.Exists(dirPath))
                                    {
                                        continue;
                                    }
                                }
                                else if (clrName[0] == '?')
                                {
                                    // Substring, search the reference base path for any folders
                                    // with a matching substring.
                                    clrName = clrName.Substring(1);

                                    var baseDirPath = referenceBasePath;
                                    var found       = false;
                                    foreach (var subdir in new DirectoryInfo(baseDirPath).GetDirectories())
                                    {
                                        if (subdir.Name.Contains(clrName))
                                        {
                                            clrName = subdir.Name;
                                            found   = true;
                                            break;
                                        }
                                    }

                                    if (!found)
                                    {
                                        continue;
                                    }
                                }
                                else
                                {
                                    throw new InvalidOperationException("Unknown CLR name match type with '" + clrName +
                                                                        "'");
                                }

                                // Otherwise enumerate through all of the libraries in this folder.
                                foreach (var dll in Directory.EnumerateFiles(
                                             Path.Combine(
                                                 referenceBasePath, clrName),
                                             "*.dll"))
                                {
                                    // Determine the relative path to the library.
                                    var packageDll = Path.Combine(
                                        referenceBasePath,
                                        clrName,
                                        Path.GetFileName(dll));

                                    // Confirm again that the file actually exists on disk when
                                    // combined with the root path.
                                    if (File.Exists(
                                            Path.Combine(
                                                packageDll)))
                                    {
                                        // Create the library reference.
                                        if (!libraryReferences.ContainsKey(Path.GetFileNameWithoutExtension(dll)))
                                        {
                                            libraryReferences.Add(
                                                Path.GetFileNameWithoutExtension(dll),
                                                packageDll);
                                        }

                                        // Mark this target framework as having provided at least
                                        // one reference.
                                        foundClr = true;
                                    }
                                }

                                // Break if we have found at least one reference.
                                if (foundClr)
                                {
                                    break;
                                }
                            }
                        }

                        // For all of the references that were found in the original nuspec file,
                        // add those references.
                        foreach (var reference in references)
                        {
                            // Search through all of the target frameworks until we find the one
                            // that has the reference in it.
                            foreach (var clrName in clrNames)
                            {
                                // If this target framework doesn't exist for this library, skip it.
                                var packageDll = Path.Combine(
                                    referenceBasePath,
                                    clrName,
                                    reference);

                                if (File.Exists(
                                        Path.Combine(
                                            packageDll)))
                                {
                                    if (!libraryReferences.ContainsKey(Path.GetFileNameWithoutExtension(packageDll)))
                                    {
                                        libraryReferences.Add(
                                            Path.GetFileNameWithoutExtension(packageDll),
                                            packageDll);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }

                foreach (var kv in libraryReferences)
                {
                    RedirectableConsole.WriteLine("Found library to reference: " + kv.Key + " (at " + kv.Value + ")");
                }

                RedirectableConsole.WriteLine("Generating external project reference...");
                var document        = new XmlDocument();
                var externalProject = document.CreateElement("ExternalProject");
                externalProject.SetAttribute("Name", packageName);
                document.AppendChild(externalProject);
                foreach (var kv in libraryReferences)
                {
                    var binaryReference = document.CreateElement("Binary");
                    binaryReference.SetAttribute("Name", kv.Key);
                    binaryReference.SetAttribute("Path",
                                                 kv.Value.Substring(folder.Length).TrimStart(new[] { '/', '\\' }).Replace("%2B", "-"));
                    externalProject.AppendChild(binaryReference);
                }
                foreach (var package in packageDependencies)
                {
                    var externalReference = document.CreateElement("Reference");
                    externalReference.SetAttribute("Include", package.Key);
                    externalProject.AppendChild(externalReference);
                }
                document.Save(Path.Combine(folder, "_ProtobuildExternalProject.xml"));

                RedirectableConsole.WriteLine("Generating module...");
                var generatedModule = new ModuleInfo();
                generatedModule.Name     = packageName;
                generatedModule.Packages = new List <PackageRef>();

                foreach (var package in packageDependencies)
                {
                    generatedModule.Packages.Add(new PackageRef
                    {
                        Uri =
                            repoUrl.Replace("http://", "http-nuget://").Replace("https://", "https-nuget://") + "|" +
                            package.Key,
                        GitRef = package.Value.TrimStart('[').TrimEnd(']'),
                        Folder = package.Key
                    });
                }

                generatedModule.Save(Path.Combine(folder, "_ProtobuildModule.xml"));

                RedirectableConsole.WriteLine("Converting to a Protobuild package...");

                var target = new MemoryStream();
                var filter = new FileFilter(_getRecursiveUtilitiesInPath.GetRecursiveFilesInPath(folder));

                foreach (var kv in libraryReferences)
                {
                    filter.ApplyInclude(
                        Regex.Escape(kv.Value.Substring(folder.Length).Replace('\\', '/').TrimStart('/')));
                    filter.ApplyRewrite(
                        Regex.Escape(kv.Value.Substring(folder.Length).Replace('\\', '/').TrimStart('/')),
                        kv.Value.Substring(folder.Length).Replace('\\', '/').TrimStart('/').Replace("%2B", "-"));
                }

                filter.ApplyInclude("_ProtobuildExternalProject\\.xml");
                filter.ApplyRewrite("_ProtobuildExternalProject\\.xml", "Build/Projects/" + packageName + ".definition");
                filter.ApplyInclude("_ProtobuildModule\\.xml");
                filter.ApplyRewrite("_ProtobuildModule\\.xml", "Build/Module.xml");

                filter.ImplyDirectories();

                _packageCreator.Create(
                    target,
                    filter,
                    folder,
                    format,
                    platform);

                RedirectableConsole.WriteLine("Package conversion complete.");
                bytes = new byte[target.Position];
                target.Seek(0, SeekOrigin.Begin);
                target.Read(bytes, 0, bytes.Length);
            }
            finally
            {
                RedirectableConsole.WriteLine("Cleaning up temporary data...");
                PathUtils.AggressiveDirectoryDelete(folder);
            }

            return(bytes);
        }
Beispiel #14
0
        private T PerformRetryableRequest <T>(string message, Uri baseUri, Func <Uri, T> func)
        {
            var exceptions = new List <Exception>();
            var backoff    = 100;

            for (var i = 0; i < MaxRequests; i++)
            {
                try
                {
                    RedirectableConsole.WriteLine("(" + (i + 1) + "/" + MaxRequests + ") " + message);
                    return(func(baseUri));
                }
                catch (Exception ex)
                {
                    exceptions.Add(ex);

                    var webException = ex as WebException;
                    if (webException != null)
                    {
                        switch (webException.Status)
                        {
                        case WebExceptionStatus.ProtocolError:
                            var httpWebResponse = webException.Response as HttpWebResponse;
                            if (httpWebResponse != null)
                            {
                                switch (httpWebResponse.StatusCode)
                                {
                                case HttpStatusCode.NotFound:
                                case HttpStatusCode.Forbidden:
                                    // This is a permanent failure.
                                    i       = MaxRequests;
                                    backoff = 0;
                                    break;
                                }
                            }
                            break;

                        case WebExceptionStatus.ServerProtocolViolation:
                        case WebExceptionStatus.NameResolutionFailure:
                            // This is a permanent failure.
                            i       = MaxRequests;
                            backoff = 0;
                            break;
                        }
                    }

                    if (!SilentOnError || i != MaxRequests || backoff != 0)
                    {
                        RedirectableConsole.ErrorWriteLine("Exception during web request: ");
                        RedirectableConsole.ErrorWriteLine(ex);

                        RedirectableConsole.WriteLine("Backing off web requests for " + backoff + "ms...");
                    }
                    System.Threading.Thread.Sleep(backoff);
                    backoff *= 2;
                    if (backoff > 20000)
                    {
                        backoff = 20000;
                    }
                }
            }

            throw new AggregateException(exceptions);
        }
Beispiel #15
0
        public int Execute(Execution execution)
        {
            var    hostPlatform          = _hostPlatformDetector.DetectPlatform();
            string builderPathNativeArch = null;
            string builderPath64         = null;
            string builderPath32         = null;
            var    extraArgsNativeArch   = string.Empty;
            var    extraArgs64           = string.Empty;
            var    extraArgs32           = string.Empty;
            var    extraArgsGeneral      = string.Empty;

            var targetPlatforms = (execution.Platform ?? hostPlatform).Split(',');
            var module          = ModuleInfo.Load(Path.Combine(execution.WorkingDirectory, "Build", "Module.xml"));

            if (hostPlatform == "Windows")
            {
                // Newer installs of Visual Studio (like 2017) don't create registry entries for MSBuild, so we have to
                // use a tool called vswhere in order to find MSBuild on these systems.  This call will implicitly install
                // the vswhere package if it's not already installed.
                var           vswhere       = _knownToolProvider.GetToolExecutablePath("vswhere");
                List <string> installations = null;
                if (vswhere != null && File.Exists(vswhere))
                {
                    try
                    {
                        var processStartInfo = new ProcessStartInfo();
                        processStartInfo.FileName               = vswhere;
                        processStartInfo.Arguments              = "-products * -requires Microsoft.Component.MSBuild -property installationPath";
                        processStartInfo.UseShellExecute        = false;
                        processStartInfo.RedirectStandardOutput = true;
                        var process             = Process.Start(processStartInfo);
                        var installationsString = process.StandardOutput.ReadToEnd();
                        installations = installationsString.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
                        process.WaitForExit();

                        if (process.ExitCode != 0)
                        {
                            RedirectableConsole.ErrorWriteLine("Unable to locate Visual Studio 2017 and later installations (non-zero exit code from vswhere)");
                        }
                    }
                    catch (Exception ex)
                    {
                        RedirectableConsole.ErrorWriteLine("Unable to locate Visual Studio 2017 and later installations: " + ex.Message);
                    }
                }

                if (installations != null)
                {
                    // Check if MSBuild is present in any of those installation paths.
                    foreach (var basePath in installations)
                    {
                        var msbuildLocation = Path.Combine(basePath, "MSBuild\\15.0\\Bin\\MSBuild.exe");
                        if (File.Exists(msbuildLocation))
                        {
                            builderPathNativeArch = msbuildLocation;
                            extraArgsNativeArch   = "/m /nodeReuse:false ";
                            builderPath32         = msbuildLocation;
                            extraArgs32           = "/m /nodeReuse:false ";

                            var x64Location = Path.Combine(basePath, "MSBuild\\15.0\\Bin\\amd64\\MSBuild.exe");
                            if (File.Exists(x64Location))
                            {
                                builderPath64 = x64Location;
                                extraArgs64   = "/m /nodeReuse:false ";
                            }

                            break;
                        }
                    }
                }

                if (builderPathNativeArch == null)
                {
                    // Try to find via the registry.
                    foreach (var arch in new[] { RegistryView.Default, RegistryView.Registry32, RegistryView.Registry64 })
                    {
                        // Find latest version of MSBuild.
                        var registryKey =
                            RegistryKey.OpenBaseKey(
                                RegistryHive.LocalMachine,
                                arch)
                            .OpenSubKey("SOFTWARE")?
                            .OpenSubKey("Microsoft")?
                            .OpenSubKey("MSBuild")?
                            .OpenSubKey("ToolsVersions");
                        if (registryKey == null)
                        {
                            if (arch == RegistryView.Registry64)
                            {
                                continue;
                            }

                            RedirectableConsole.ErrorWriteLine(
                                "ERROR: No versions of MSBuild were available " +
                                "according to the registry (or they were not readable).");
                            return(1);
                        }

                        var subkeys         = registryKey.GetSubKeyNames();
                        var orderedVersions =
                            subkeys.OrderByDescending(x => int.Parse(x.Split('.').First(), CultureInfo.InvariantCulture));
                        var builderPath = (from version in orderedVersions
                                           let path = (string)registryKey.OpenSubKey(version)?.GetValue("MSBuildToolsPath")
                                                      where path != null && Directory.Exists(path)
                                                      let msbuild = Path.Combine(path, "MSBuild.exe")
                                                                    where File.Exists(msbuild)
                                                                    select msbuild).FirstOrDefault();

                        if (builderPath == null)
                        {
                            if (arch == RegistryView.Registry64)
                            {
                                continue;
                            }

                            RedirectableConsole.ErrorWriteLine(
                                "ERROR: Unable to find installed MSBuild in any installed tools version.");
                            return(1);
                        }

                        var extraArgs = string.Empty;
                        if (!builderPath.Contains("v2.0.50727"))
                        {
                            extraArgs = "/m /nodeReuse:false ";
                        }

                        switch (arch)
                        {
                        case RegistryView.Default:
                            builderPathNativeArch = builderPath;
                            extraArgsNativeArch   = extraArgs;
                            break;

                        case RegistryView.Registry32:
                            builderPath32 = builderPath;
                            extraArgs32   = extraArgs;
                            break;

                        case RegistryView.Registry64:
                            builderPath64 = builderPath;
                            extraArgs64   = extraArgs;
                            break;
                        }
                    }
                }
            }
            else
            {
                // Find path to xbuild.
                var whichPaths = new[] { "/bin/which", "/usr/bin/which" };

                // We can only use the new MSBuild tool if no projects are C++ projects on Mac or Linux.
                var isAnyNativeProject = false;
                foreach (var def in module.GetDefinitionsRecursively())
                {
                    var document     = XDocument.Load(def.DefinitionPath);
                    var languageAttr = document?.Root?.Attributes()?.FirstOrDefault(x => x.Name.LocalName == "Language");
                    if (languageAttr != null && languageAttr.Value == "C++")
                    {
                        isAnyNativeProject = true;
                        break;
                    }
                }
                if (!isAnyNativeProject)
                {
                    foreach (var w in whichPaths)
                    {
                        if (File.Exists(w))
                        {
                            var whichProcess = Process.Start(new ProcessStartInfo(w, "msbuild")
                            {
                                RedirectStandardOutput = true,
                                UseShellExecute        = false
                            });
                            if (whichProcess == null)
                            {
                                continue;
                            }
                            var result = whichProcess.StandardOutput.ReadToEnd().Trim();
                            if (!string.IsNullOrWhiteSpace(result) && File.Exists(result))
                            {
                                builderPathNativeArch = result;
                                break;
                            }
                        }
                    }
                }

                if (builderPathNativeArch == null)
                {
                    foreach (var w in whichPaths)
                    {
                        if (File.Exists(w))
                        {
                            var whichProcess = Process.Start(new ProcessStartInfo(w, "xbuild")
                            {
                                RedirectStandardOutput = true,
                                UseShellExecute        = false
                            });
                            if (whichProcess == null)
                            {
                                continue;
                            }
                            var result = whichProcess.StandardOutput.ReadToEnd().Trim();
                            if (!string.IsNullOrWhiteSpace(result) && File.Exists(result))
                            {
                                builderPathNativeArch = result;
                                break;
                            }
                        }
                    }
                }

                if (builderPathNativeArch == null && _hostPlatformDetector.DetectPlatform() == "MacOS" && File.Exists("/usr/local/bin/xbuild"))
                {
                    // After upgrading to OSX El Capitan, the /usr/local/bin folder is no longer in
                    // the system PATH.  If we can't find xbuild with the which tool, manually set the
                    // path here in an attempt to find it.
                    builderPathNativeArch = "/usr/local/bin/xbuild";
                }

                if (builderPathNativeArch == null)
                {
                    RedirectableConsole.ErrorWriteLine("ERROR: Unable to find msbuild or xbuild on the current PATH.");
                    return(1);
                }

                builderPath32 = builderPathNativeArch;
                builderPath64 = builderPathNativeArch;
            }

            if (!string.IsNullOrWhiteSpace(execution.BuildTarget))
            {
                extraArgsGeneral += "/t:\"" + execution.BuildTarget + "\" ";
            }
            foreach (var prop in execution.BuildProperties)
            {
                extraArgsGeneral += "/p:\"" + prop.Key.Replace("\"", "\\\"") + "\"=\"" + (prop.Value ?? string.Empty).Replace("\"", "\\\"") + "\" ";
            }

            switch (execution.BuildProcessArchitecture)
            {
            case "x86":
                RedirectableConsole.WriteLine("INFO: Using " + builderPath32 + " (forced 32-bit) to perform this build.");
                break;

            case "x64":
                RedirectableConsole.WriteLine("INFO: Using " + builderPath64 + " (forced 64-bit) to perform this build.");
                break;

            case "Default":
            default:
                RedirectableConsole.WriteLine("INFO: Using " + builderPathNativeArch + " (32-bit: " + builderPath32 + ") to perform this build.");
                break;
            }

            foreach (var platform in targetPlatforms)
            {
                string builderPath;
                string extraArgs;

                switch (execution.BuildProcessArchitecture)
                {
                case "x86":
                    builderPath = builderPath32;
                    extraArgs   = extraArgs32 + extraArgsGeneral;
                    break;

                case "x64":
                    builderPath = builderPath64;
                    extraArgs   = extraArgs64 + extraArgsGeneral;
                    break;

                case "Default":
                default:
                    builderPath = platform == "WindowsPhone" ? builderPath32 : builderPathNativeArch;
                    extraArgs   = (platform == "WindowsPhone" ? extraArgs32 : extraArgsNativeArch) + extraArgsGeneral;
                    break;
                }

                var fileToBuild = module.Name + "." + platform + ".sln";

                RedirectableConsole.WriteLine("INFO: Executing " + builderPath + " with arguments: " + extraArgs + fileToBuild);

                var process =
                    Process.Start(new ProcessStartInfo(builderPath, extraArgs + fileToBuild)
                {
                    UseShellExecute = false
                });
                if (process == null)
                {
                    RedirectableConsole.ErrorWriteLine("ERROR: Build process did not start successfully.");
                    return(1);
                }
                process.WaitForExit();

                if (process.ExitCode != 0)
                {
                    return(process.ExitCode);
                }
            }

            return(0);
        }
Beispiel #16
0
        public void Resolve(string workingDirectory, IPackageMetadata metadata, PackageRef reference, string templateName, bool?source,
                            bool forceUpgrade, bool?safeResolve)
        {
            if (reference.Folder == null)
            {
                if (metadata.PackageType == PACKAGE_TYPE_GLOBAL_TOOL)
                {
                }
                else
                {
                    throw new InvalidOperationException(
                              "No target folder was provided for package resolution, and the resulting package is not " +
                              "a global tool.");
                }
            }
            else
            {
                if (metadata.PackageType == PackageManager.PACKAGE_TYPE_TEMPLATE && templateName == null)
                {
                    throw new InvalidOperationException(
                              "Template referenced as part of module packages.  Templates can only be used " +
                              "with the --start option.");
                }
                else if (metadata.PackageType == PackageManager.PACKAGE_TYPE_LIBRARY)
                {
                    Directory.CreateDirectory(Path.Combine(workingDirectory, reference.Folder));

                    if (new DirectoryInfo(Path.Combine(workingDirectory, reference.Folder)).GetFiles().Length > 0 || new DirectoryInfo(Path.Combine(workingDirectory, reference.Folder)).GetDirectories().Length > 0)
                    {
                        if (!File.Exists(Path.Combine(workingDirectory, reference.Folder, ".git")) && !Directory.Exists(Path.Combine(workingDirectory, reference.Folder, ".git")) &&
                            !File.Exists(Path.Combine(workingDirectory, reference.Folder, ".pkg")))
                        {
                            bool shouldSafeResolve;
                            if (safeResolve.HasValue)
                            {
                                // If the user specifies it on the command line, use that setting.
                                shouldSafeResolve = safeResolve.Value;
                            }
                            else
                            {
                                if (!_featureManager.IsFeatureEnabled(Feature.SafeResolutionDisabled))
                                {
                                    // If the module doesn't have this feature set enabled, we default
                                    // to using safe package resolution.
                                    shouldSafeResolve = true;
                                }
                                else
                                {
                                    // If the module does have this feature set enabled, or is using the
                                    // full feature set, we default to turning safe resolution off.
                                    shouldSafeResolve = false;
                                }
                            }

                            if (shouldSafeResolve)
                            {
                                RedirectableConsole.ErrorWriteLine(
                                    "WARNING: The package directory '" + reference.Folder +
                                    "' already exists and contains " +
                                    "files and/or subdirectories, but neither a .pkg file nor a .git file or subdirectory exists.  " +
                                    "This indicates the package directory contains data that is not been instantiated or managed " +
                                    "by Protobuild.  Since there is no safe way to initialize the package in this directory " +
                                    "without a potential loss of data, Protobuild will not modify the contents of this folder " +
                                    "during package resolution.  If the folder does not contains the required package " +
                                    "dependencies, the project generation or build may unexpectedly fail.");
                                return;
                            }
                        }
                    }

                    if (source == null)
                    {
                        if (File.Exists(Path.Combine(workingDirectory, reference.Folder, ".git")) || Directory.Exists(Path.Combine(workingDirectory, reference.Folder, ".git")))
                        {
                            RedirectableConsole.WriteLine("Git repository present at " + Path.Combine(workingDirectory, reference.Folder, ".git") + "; leaving as source version.");
                            source = true;
                        }
                        else
                        {
                            RedirectableConsole.WriteLine("Package type not specified (and no file at " + Path.Combine(workingDirectory, reference.Folder, ".git") + "), requesting binary version.");
                            source = false;
                        }
                    }
                }
            }

            metadata.Resolve(workingDirectory, metadata, reference.Folder, templateName, forceUpgrade, source);
        }
        /// <summary>
        /// Generates a project at the target path.
        /// </summary>
        /// <param name="current">The current project to generate.</param>
        /// <param name="definitions">A list of all loaded project definitions.</param>
        /// <param name="workingDirectory"></param>
        /// <param name="rootPath">The module root path, with directory seperator appended.</param>
        /// <param name="projectName">The project name.</param>
        /// <param name="platformName">The platform name.</param>
        /// <param name="services">A list of services.</param>
        /// <param name="packagesFilePath">
        ///     Either the full path to the packages.config for the
        ///     generated project if it exists, or an empty string.
        /// </param>
        /// <param name="onActualGeneration"></param>
        /// <param name="debugProjectGeneration">Whether to emit a .input file for XSLT debugging.</param>
        public void Generate(
            DefinitionInfo current,
            List <LoadedDefinitionInfo> definitions,
            string workingDirectory,
            string rootPath,
            string projectName,
            string platformName,
            List <Service> services,
            out string packagesFilePath,
            Action onActualGeneration,
            bool debugProjectGeneration)
        {
            packagesFilePath = string.Empty;

            // Work out what document this is.
            var projectDoc = definitions.First(
                x => x.Project.DocumentElement.Attributes["Name"].Value == projectName)?.Project;

            // Check to see if we have a Project node; don't process it.
            if (projectDoc?.DocumentElement == null)
            {
                return;
            }

            // If this is a custom project, run any custom generation commands for
            // the current host and target platform.
            if (projectDoc.DocumentElement.Name == "CustomProject")
            {
                onActualGeneration();

                var onGenerateElements = projectDoc.DocumentElement.SelectNodes("OnGenerate");
                if (onGenerateElements != null)
                {
                    foreach (var onGenerateElement in onGenerateElements.OfType <XmlElement>())
                    {
                        var matches    = true;
                        var hostName   = onGenerateElement.GetAttribute("HostName");
                        var targetName = onGenerateElement.GetAttribute("TargetName");

                        if (!string.IsNullOrWhiteSpace(hostName))
                        {
                            if (hostName != _hostPlatformDetector.DetectPlatform())
                            {
                                matches = false;
                            }
                        }

                        if (!string.IsNullOrWhiteSpace(targetName))
                        {
                            if (targetName != platformName)
                            {
                                matches = false;
                            }
                        }

                        if (matches)
                        {
                            var command   = onGenerateElement.SelectSingleNode("Command")?.InnerText.Trim();
                            var arguments = onGenerateElement.SelectSingleNode("Arguments")?.InnerText.Trim() ?? string.Empty;

                            if (!string.IsNullOrWhiteSpace(command))
                            {
                                var workingPath = Path.Combine(
                                    rootPath,
                                    projectDoc.DocumentElement.Attributes["Path"].Value);
                                var resolvedCommandPath = Path.Combine(
                                    workingPath,
                                    command);

                                if (File.Exists(resolvedCommandPath))
                                {
                                    var process =
                                        Process.Start(new ProcessStartInfo(resolvedCommandPath, arguments)
                                    {
                                        WorkingDirectory = workingPath,
                                        UseShellExecute  = false
                                    });
                                    if (process == null)
                                    {
                                        RedirectableConsole.ErrorWriteLine("ERROR: Process did not start when running " + resolvedCommandPath + " " +
                                                                           arguments);
                                        ExecEnvironment.Exit(1);
                                        return;
                                    }
                                    process.WaitForExit();
                                    if (process.ExitCode != 0)
                                    {
                                        RedirectableConsole.ErrorWriteLine(
                                            "ERROR: Non-zero exit code " + process.ExitCode);
                                        ExecEnvironment.Exit(1);
                                        return;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // If this is not a normal project at this point, don't process it.
            if (projectDoc.DocumentElement.Name != "Project")
            {
                return;
            }

            // Load the appropriate project transformation XSLT.
            var languageAttribute = projectDoc.DocumentElement.Attributes["Language"];
            var languageText      = languageAttribute != null ? languageAttribute.Value : "C#";
            var language          = this.m_LanguageStringProvider.GetLanguageFromConfigurationName(languageText);
            var projectTransform  = this.m_ResourceProvider.LoadXSLT(workingDirectory, ResourceType.GenerateProject, language, platformName);

            // Work out what platforms this project should be generated for.
            var platformAttribute = projectDoc.DocumentElement.Attributes["Platforms"];

            string[] allowedPlatforms = null;
            if (platformAttribute != null)
            {
                allowedPlatforms = platformAttribute.Value
                                   .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                   .Select(x => x.Trim())
                                   .ToArray();
            }

            // Filter on allowed platforms.
            if (allowedPlatforms != null)
            {
                var allowed = false;
                foreach (var platform in allowedPlatforms)
                {
                    if (string.Compare(platformName, platform, StringComparison.InvariantCultureIgnoreCase) == 0)
                    {
                        allowed = true;
                        break;
                    }
                }
                if (!allowed)
                {
                    return;
                }
            }

            // If the project has a <Services> node, but there are no entries in /Input/Services,
            // then nothing depends on this service (if there is a <Reference> to this project, it will
            // use the default service).  So for service-aware projects without any services being
            // referenced, we exclude them from the generation.
            if (this.m_ExcludedServiceAwareProjectDetector.IsExcludedServiceAwareProject(
                    projectDoc.DocumentElement.Attributes["Name"].Value,
                    projectDoc,
                    services))
            {
                return;
            }

            // Inform the user we're generating this project.
            onActualGeneration();

            // Add include projects if they have an AppliesTo tag that matches this project's name.
            this._includeProjectAppliesToUpdater.UpdateProjectReferences(definitions.Select(x => x.Project).ToList(), projectDoc);

            // Add references and properties from include projects.
            _includeProjectMerger.MergeInReferencesAndPropertiesForIncludeProjects(definitions, projectDoc, platformName);

            // Imply external project references from other external projects.  We do
            // this so that external projects can reference other external projects (which
            // we can't reasonably handle at the XSLT level since it's recursive).
            this.m_ExternalProjectReferenceResolver.ResolveExternalProjectReferences(definitions, projectDoc, platformName);

            // Generate Info.plist files if necessary (for Mac / iOS).
            this._mPlatformResourcesGenerator.GenerateInfoPListIfNeeded(definitions, current, projectDoc, platformName);

            // Work out what path to save at.
            var path = Path.Combine(
                rootPath,
                projectDoc.DocumentElement.Attributes["Path"].Value
                .Replace('\\', Path.DirectorySeparatorChar)
                .Replace('/', Path.DirectorySeparatorChar),
                projectDoc.DocumentElement.Attributes["Name"].Value + "." +
                platformName + "." + this.m_LanguageStringProvider.GetProjectExtension(language));

            // Make sure that the directory exists where the file will be stored.
            var targetFile = new FileInfo(path);

            if (!targetFile.Directory.Exists)
            {
                targetFile.Directory.Create();
            }

            path = targetFile.FullName;

            // Handle NuGet packages.config early so that it'll be in place
            // when the generator automatically determined dependencies.
            this.m_NuGetConfigMover.Move(rootPath, platformName, projectDoc);

            // Work out what path the NuGet packages.config might be at.
            var packagesFile = new FileInfo(
                Path.Combine(
                    rootPath,
                    projectDoc.DocumentElement.Attributes["Path"].Value
                    .Replace('\\', Path.DirectorySeparatorChar)
                    .Replace('/', Path.DirectorySeparatorChar),
                    "packages.config"));

            // Generate the input document.
            var input = this.m_ProjectInputGenerator.Generate(
                definitions.Select(x => x.Project).ToList(),
                workingDirectory,
                rootPath,
                projectName,
                platformName,
                packagesFile.FullName,
                projectDoc.DocumentElement.ChildNodes
                .OfType <XmlElement>()
                .Where(x => x.Name.ToLower() == "properties")
                .SelectMany(x => x.ChildNodes
                            .OfType <XmlElement>()),
                services);

            var settings = new XmlWriterSettings();

            settings.Indent = true;

            if (debugProjectGeneration)
            {
                using (var writer = XmlWriter.Create(path + ".input", settings))
                {
                    input.Save(writer);
                }
            }

            // Transform the input document using the XSLT transform.
            using (var writer = XmlWriter.Create(path, settings))
            {
                projectTransform.Transform(input, writer);
            }

            // Also remove any left over .sln or .userprefs files.
            var slnPath = Path.Combine(
                rootPath,
                projectDoc.DocumentElement.Attributes["Path"].Value,
                projectDoc.DocumentElement.Attributes["Name"].Value + "." +
                platformName + ".sln");
            var userprefsPath = Path.Combine(
                rootPath,
                projectDoc.DocumentElement.Attributes["Path"].Value,
                projectDoc.DocumentElement.Attributes["Name"].Value + "." +
                platformName + ".userprefs");

            if (File.Exists(slnPath))
            {
                File.Delete(slnPath);
            }
            if (File.Exists(userprefsPath))
            {
                File.Delete(userprefsPath);
            }

            // Only return the package file path if it exists.
            if (packagesFile.Exists)
            {
                packagesFilePath = packagesFile.FullName;
            }
        }
        public Tuple <int, string, string> RunProtobuild(ModuleInfo module, string args, bool capture = false)
        {
            var invokeInline = false;
            var myHash       = ExecEnvironment.GetProgramHash();
            var targetHash   = ExecEnvironment.GetProgramHash(Path.Combine(module.Path, "Protobuild.exe"));

            if (myHash == targetHash)
            {
                invokeInline = true;
            }

            if (ExecEnvironment.RunProtobuildInProcess || invokeInline)
            {
                var oldBuffer = RedirectableConsole.TargetBuffer;
                var ourBuffer = new RedirectableBuffer();
                RedirectableConsole.TargetBuffer = ourBuffer;
                var needsEndSelfInvoke = true;
                try
                {
                    var exitCode = ExecEnvironment.InvokeSelf(module.Path, args.SplitCommandLine().ToArray());
                    RedirectableConsole.TargetBuffer = oldBuffer;
                    needsEndSelfInvoke = false;
                    if (capture)
                    {
                        return(new Tuple <int, string, string>(
                                   exitCode,
                                   ourBuffer.Stdout,
                                   ourBuffer.Stderr));
                    }
                    else
                    {
                        return(new Tuple <int, string, string>(
                                   exitCode,
                                   string.Empty,
                                   string.Empty));
                    }
                }
                finally
                {
                    if (needsEndSelfInvoke)
                    {
                        RedirectableConsole.TargetBuffer = oldBuffer;
                    }
                }
            }

            var protobuildPath = Path.Combine(module.Path, "Protobuild.exe");

            try
            {
                var chmodStartInfo = new ProcessStartInfo
                {
                    FileName         = "chmod",
                    Arguments        = "a+x Protobuild.exe",
                    WorkingDirectory = module.Path,
                    CreateNoWindow   = capture,
                    UseShellExecute  = false
                };
                Process.Start(chmodStartInfo);
            }
            catch (ExecEnvironment.SelfInvokeExitException)
            {
                throw;
            }
            catch
            {
            }

            var stdout = string.Empty;
            var stderr = string.Empty;

            for (var attempt = 0; attempt < 3; attempt++)
            {
                if (File.Exists(protobuildPath))
                {
                    var pi = new ProcessStartInfo
                    {
                        FileName               = protobuildPath,
                        Arguments              = args,
                        WorkingDirectory       = module.Path,
                        CreateNoWindow         = capture,
                        RedirectStandardError  = capture,
                        RedirectStandardInput  = capture,
                        RedirectStandardOutput = capture,
                        UseShellExecute        = false
                    };
                    var p = new Process {
                        StartInfo = pi
                    };
                    if (capture)
                    {
                        p.OutputDataReceived += (sender, eventArgs) =>
                        {
                            if (!string.IsNullOrEmpty(eventArgs.Data))
                            {
                                if (capture)
                                {
                                    stdout += eventArgs.Data + "\n";
                                }
                                else
                                {
                                    RedirectableConsole.WriteLine(eventArgs.Data);
                                }
                            }
                        };
                        p.ErrorDataReceived += (sender, eventArgs) =>
                        {
                            if (!string.IsNullOrEmpty(eventArgs.Data))
                            {
                                if (capture)
                                {
                                    stderr += eventArgs.Data + "\n";
                                }
                                else
                                {
                                    RedirectableConsole.ErrorWriteLine(eventArgs.Data);
                                }
                            }
                        };
                    }
                    try
                    {
                        p.Start();
                    }
                    catch (System.ComponentModel.Win32Exception ex)
                    {
                        if (ex.Message.Contains("Cannot find the specified file"))
                        {
                            // Mono sometimes throws this error even though the
                            // file does exist on disk.  The best guess is there's
                            // a race condition between performing chmod on the
                            // file and Mono actually seeing it as an executable file.
                            // Show a warning and sleep for a bit before retrying.
                            if (attempt != 2)
                            {
                                RedirectableConsole.WriteLine("WARNING: Unable to execute Protobuild.exe, will retry again...");
                                System.Threading.Thread.Sleep(2000);
                                continue;
                            }
                            else
                            {
                                RedirectableConsole.WriteLine("ERROR: Still unable to execute Protobuild.exe.");
                                throw;
                            }
                        }
                    }
                    if (capture)
                    {
                        p.BeginOutputReadLine();
                        p.BeginErrorReadLine();
                    }
                    p.WaitForExit();
                    return(new Tuple <int, string, string>(p.ExitCode, stdout, stderr));
                }
            }

            return(new Tuple <int, string, string>(1, string.Empty, string.Empty));
        }
        public int Execute(string workingDirectory, object handle)
        {
            var instructions = (List <ParsedInstruction>)handle;

            var protobuild = Assembly.GetEntryAssembly().Location;

            var    targets              = string.Empty;
            var    buildTarget          = string.Empty;
            var    buildProcessArch     = string.Empty;
            var    buildProperties      = new Dictionary <string, string>();
            string executeConfiguration = null;
            string packageFormat        = null;

            var predicates = new Stack <ParsedInstruction>();

            foreach (var inst in instructions)
            {
                if (inst.Predicate != null)
                {
                    predicates.Push(inst);
                }
                else if (inst.EndPredicate)
                {
                    predicates.Pop();
                }
                else if (predicates.All(x => x.Predicate()))
                {
                    if (inst.Command == "native-execute")
                    {
                        var components = inst.Arguments.Split(new[] { ' ' }, 2);

                        string path;
                        try
                        {
                            path = FindNativeProgram(components[0]);
                        }
                        catch (ApplicationException ex)
                        {
                            RedirectableConsole.ErrorWriteLine(ex);
                            return(1);
                        }

                        var args = components.Length == 2 ? components[1] : string.Empty;

                        RedirectableConsole.WriteLine("+ native-execute " + path + " " + args);
                        var process =
                            Process.Start(new ProcessStartInfo(path, args)
                        {
                            WorkingDirectory = workingDirectory,
                            UseShellExecute  = false
                        });
                        if (process == null)
                        {
                            RedirectableConsole.ErrorWriteLine("ERROR: Process did not start when running " + path + " " +
                                                               args);
                            return(1);
                        }
                        process.WaitForExit();
                        if (process.ExitCode != 0)
                        {
                            RedirectableConsole.ErrorWriteLine(
                                "ERROR: Non-zero exit code " + process.ExitCode);
                            return(process.ExitCode);
                        }
                    }
                    else if (inst.Command == "nuget")
                    {
                        // See if we have a copy of NuGet available for use.
                        var cachedNuget =
                            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                                         "NuGet.exe");
                        if (!File.Exists(cachedNuget))
                        {
                            var client = new RetryableWebClient();
                            client.DownloadFile("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", cachedNuget);
                        }

                        string runtime      = null;
                        var    hostPlatform = _hostPlatformDetector.DetectPlatform();
                        if (hostPlatform != "Windows")
                        {
                            try
                            {
                                runtime = FindNativeProgram("mono");
                            }
                            catch (ApplicationException ex)
                            {
                                RedirectableConsole.ErrorWriteLine(ex);
                                return(1);
                            }
                        }

                        Process process;

                        if (hostPlatform != "Windows" && runtime != null)
                        {
                            RedirectableConsole.WriteLine("+ " + runtime + " \"" + cachedNuget + "\" " + inst.Arguments);
                            process =
                                Process.Start(new ProcessStartInfo(runtime, "\"" + cachedNuget + "\" " + inst.Arguments)
                            {
                                WorkingDirectory = workingDirectory,
                                UseShellExecute  = false
                            });
                        }
                        else
                        {
                            RedirectableConsole.WriteLine("+ " + cachedNuget + " " + inst.Arguments);
                            process =
                                Process.Start(new ProcessStartInfo(cachedNuget, inst.Arguments)
                            {
                                WorkingDirectory = workingDirectory,
                                UseShellExecute  = false
                            });
                        }

                        if (process == null)
                        {
                            RedirectableConsole.ErrorWriteLine(
                                "ERROR: Process did not start when running NuGet with arguments " + inst.Arguments);
                            return(1);
                        }
                        process.WaitForExit();
                        if (process.ExitCode != 0)
                        {
                            RedirectableConsole.ErrorWriteLine(
                                "ERROR: Non-zero exit code " + process.ExitCode);
                            return(process.ExitCode);
                        }
                    }
                    else if (inst.Command != null)
                    {
                        var args = string.Empty;
                        switch (inst.Command)
                        {
                        case "execute":
                            if (executeConfiguration != null)
                            {
                                args = "--execute-configuration " + executeConfiguration + " --" + inst.Command +
                                       " " + inst.Arguments;
                            }
                            else
                            {
                                args = "--" + inst.Command + " " + inst.Arguments;
                            }
                            break;

                        case "build":
                            args = "--" + inst.Command + " " + targets + " ";

                            if (buildTarget != string.Empty)
                            {
                                args += "--build-target " + buildTarget + " ";
                            }

                            if (buildProcessArch != string.Empty)
                            {
                                args += "--build-process-arch " + buildProcessArch + " ";
                            }

                            args = buildProperties.Aggregate(args,
                                                             (current, prop) =>
                                                             current + ("--build-property " + prop.Key + " " + prop.Value + " "));
                            args += " " + inst.Arguments;
                            break;

                        case "pack":
                        case "push":
                        case "repush":
                            args = string.Empty;
                            if (!string.IsNullOrWhiteSpace(packageFormat))
                            {
                                args = "--format " + packageFormat + " ";
                            }
                            args += "--" + inst.Command + " " + inst.Arguments;
                            break;

                        default:
                            args = "--" + inst.Command + " " + targets + " " + inst.Arguments;
                            break;
                        }

                        var runSets = new List <string>();
                        if (args.Contains("$TARGET_PLATFORM"))
                        {
                            var targetsSplit = targets.Split(',');
                            if (targetsSplit.Length < 1 || string.IsNullOrWhiteSpace(targetsSplit[0]))
                            {
                                runSets.Add(_hostPlatformDetector.DetectPlatform());
                            }
                            else
                            {
                                runSets.AddRange(targetsSplit);
                            }
                        }
                        else
                        {
                            runSets.Add(_hostPlatformDetector.DetectPlatform());
                        }

                        if (args.Contains("$GIT_BRANCH"))
                        {
                            RedirectableConsole.ErrorWriteLine(
                                "ERROR: Support for $GIT_BRANCH has been dropped, because it almost never behaved as intended (due to no guarentees that the desired refs would exist in the repository being operated on).");
                            return(1);
                        }

                        if (args.Contains("$GIT_COMMIT") || inst.Command == "pack")
                        {
                            string commit;

                            try
                            {
                                RedirectableConsole.WriteLine("+ git rev-parse HEAD");
                                commit = GitUtils.RunGitAndCapture(workingDirectory, null, "rev-parse HEAD").Trim();
                            }
                            catch (InvalidOperationException)
                            {
                                commit = string.Empty;
                            }

                            if (inst.Command == "pack" && !string.IsNullOrWhiteSpace(commit))
                            {
                                args = "--package-git-commit " + commit + " " + args;
                            }

                            args = args.Replace("$GIT_COMMIT", commit);
                        }

                        string runtime      = null;
                        var    hostPlatform = _hostPlatformDetector.DetectPlatform();
                        if (hostPlatform != "Windows")
                        {
                            try
                            {
                                runtime = FindNativeProgram("mono");
                            }
                            catch (ApplicationException ex)
                            {
                                RedirectableConsole.ErrorWriteLine(ex);
                                return(1);
                            }
                        }

                        foreach (var run in runSets)
                        {
                            var runArgs = args
                                          .Replace("$TARGET_PLATFORM", run);

                            Process process;

                            if (hostPlatform != "Windows" && runtime != null)
                            {
                                RedirectableConsole.WriteLine("+ " + runtime + " \"" + protobuild + "\" " + runArgs);
                                process =
                                    Process.Start(new ProcessStartInfo(runtime, "\"" + protobuild + "\" " + runArgs)
                                {
                                    WorkingDirectory = workingDirectory,
                                    UseShellExecute  = false
                                });
                            }
                            else
                            {
                                RedirectableConsole.WriteLine("+ " + protobuild + " " + runArgs);
                                process =
                                    Process.Start(new ProcessStartInfo(protobuild, runArgs)
                                {
                                    WorkingDirectory = workingDirectory,
                                    UseShellExecute  = false
                                });
                            }

                            if (process == null)
                            {
                                RedirectableConsole.ErrorWriteLine(
                                    "ERROR: Process did not start when running Protobuild with arguments " + args);
                                return(1);
                            }
                            process.WaitForExit();
                            if (process.ExitCode != 0)
                            {
                                RedirectableConsole.ErrorWriteLine(
                                    "ERROR: Non-zero exit code " + process.ExitCode);
                                return(process.ExitCode);
                            }
                        }
                    }
                    else if (inst.Key != null)
                    {
                        RedirectableConsole.WriteLine("+ set " + inst.Key + " -> " + inst.Values.Aggregate((a, b) => a + ", " + b));
                        switch (inst.Key)
                        {
                        case "target-platforms":
                            targets = inst.Values.Aggregate((a, b) => a + "," + b);
                            break;

                        case "build-target":
                            buildTarget = inst.Values.First();
                            break;

                        case "build-process-arch":
                            buildProcessArch = inst.Values.First();
                            break;

                        case "build-property":
                            buildProperties.Add(inst.Values[0],
                                                inst.Values.Length >= 2 ? inst.Values[1] : string.Empty);
                            break;

                        case "execute-configuration":
                            executeConfiguration = inst.Values.First();
                            break;

                        case "package-format":
                            packageFormat = inst.Values.First();
                            break;
                        }
                    }
                    else if (inst.Echo != null)
                    {
                        RedirectableConsole.WriteLine(inst.Echo);
                    }
                }
            }

            RedirectableConsole.WriteLine("Automated build script completed successfully.");
            return(0);
        }
        /// <summary>
        /// Performs a resynchronisation, synchronisation, generation or clean on the specified module.
        /// </summary>
        /// <returns><c>true</c>, if the action succeeded, <c>false</c> otherwise.</returns>
        /// <param name="module">The module to perform the action on.</param>
        /// <param name="action">The action to perform, either "resync", "sync", "generate" or "clean".</param>
        /// <param name="platform">The platform to perform the action for.</param>
        /// <param name="enabledServices">A list of enabled services.</param>
        /// <param name="disabledServices">A list of disabled services.</param>
        /// <param name="serviceSpecPath">The service specification path.</param>
        /// <param name="debugServiceResolution">Whether to enable debugging information during service resolution.</param>
        /// <param name="disablePackageResolution">Whether to disable package resolution.</param>
        /// <param name="disableHostPlatformGeneration">Whether to disable generation of the host platform projects.</param>
        /// <param name="taskParallelisation">Whether to enable or disable task generation, or null for the default behaviour.</param>
        public bool PerformAction(
            string workingDirectory,
            ModuleInfo module,
            string action,
            string platform,
            string[] enabledServices,
            string[] disabledServices,
            string serviceSpecPath,
            bool debugServiceResolution,
            bool disablePackageResolution,
            bool disableHostPlatformGeneration,
            bool?taskParallelisation,
            bool?safeResolve,
            bool debugProjectGeneration)
        {
            var platformSupplied = !string.IsNullOrWhiteSpace(platform);

            var hostPlatform = this.m_HostPlatformDetector.DetectPlatform();

            if (string.IsNullOrWhiteSpace(platform))
            {
                platform = hostPlatform;
            }

            var    originalPlatform  = platform;
            string primaryPlatform   = null;
            string multiplePlatforms = null;

            if (platform.Contains(","))
            {
                // The user requested multiple platforms; the first one
                // is the primary platform.
                var platforms = platform.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                if (platforms.Length == 0)
                {
                    RedirectableConsole.ErrorWriteLine("You supplied only commas where a list of platforms was expected.");
                    ExecEnvironment.Exit(1);
                    return(false);
                }
                else
                {
                    for (var i = 0; i < platforms.Length; i++)
                    {
                        var newPlatform = _moduleUtilities.NormalizePlatform(module, platforms[i]);
                        if (newPlatform == null)
                        {
                            ShowSupportedPlatformsError(module, platforms[i]);
                            return(false);
                        }
                        platforms[i] = newPlatform;
                    }

                    primaryPlatform   = platforms[0];
                    multiplePlatforms = platforms.Aggregate((a, b) => a + "," + b);
                }
            }
            else
            {
                platform = _moduleUtilities.NormalizePlatform(module, platform);

                if (platform == null && !platformSupplied)
                {
                    // The current host platform isn't supported, so we shouldn't try to
                    // operate on it.
                    string firstPlatform = null;
                    switch (this.m_HostPlatformDetector.DetectPlatform())
                    {
                    case "Windows":
                        firstPlatform = module.DefaultWindowsPlatforms.Split(',').FirstOrDefault();
                        break;

                    case "MacOS":
                        firstPlatform = module.DefaultMacOSPlatforms.Split(',').FirstOrDefault();
                        break;

                    case "Linux":
                        firstPlatform = module.DefaultLinuxPlatforms.Split(',').FirstOrDefault();
                        break;
                    }

                    if (firstPlatform != null)
                    {
                        // This will end up null if the first platform isn't supported
                        // either and hence throw the right message.
                        platform = _moduleUtilities.NormalizePlatform(module, firstPlatform);
                    }
                }

                if (platform == null)
                {
                    ShowSupportedPlatformsError(module, originalPlatform);
                    return(false);
                }

                primaryPlatform = platform;
            }

            // You can generate multiple targets by default by setting the <DefaultWindowsPlatforms>
            // <DefaultMacOSPlatforms> and <DefaultLinuxPlatforms> tags in Module.xml.  Note that
            // synchronisation will only be done for the primary platform, as there is no correct
            // synchronisation behaviour when dealing with multiple C# projects.
            //
            // We only trigger this behaviour when the platform is omitted; if you explicitly
            // specify "Windows" on the command line, we'll only generate / resync / sync
            // the Windows platform.
            if (!platformSupplied)
            {
                switch (platform)
                {
                case "Windows":
                    multiplePlatforms = module.DefaultWindowsPlatforms;
                    break;

                case "MacOS":
                    multiplePlatforms = module.DefaultMacOSPlatforms;
                    break;

                case "Linux":
                    multiplePlatforms = module.DefaultLinuxPlatforms;
                    break;
                }
            }

            // If no overrides are set, just use the current platform.
            if (string.IsNullOrEmpty(multiplePlatforms))
            {
                multiplePlatforms = platform;
            }

            // If running pure synchronisation or a project clean, we don't need to perform
            // package resolution.
            if (action.ToLower() == "sync" || action.ToLower() == "clean")
            {
                disablePackageResolution = true;
            }

            // Resolve submodules as needed.
            if (!disablePackageResolution)
            {
                this.m_PackageManager.ResolveAll(workingDirectory, module, primaryPlatform, taskParallelisation, false, safeResolve);
            }

            // Create the list of multiple platforms.
            var multiplePlatformsList =
                multiplePlatforms.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();

            // Remember whether or not we need to implicitly generate the host
            // platform.
            var    implicitlyGenerateHostPlatform = false;
            Action requiresHostPlatform           = () => implicitlyGenerateHostPlatform = true;

            // If we are already generating the host platform, then requiring the
            // host platform is already satisifed.
            if (platform == hostPlatform || multiplePlatformsList.Contains(hostPlatform))
            {
                requiresHostPlatform = () => {};
            }
            else if (!_featureManager.IsFeatureEnabled(Feature.HostPlatformGeneration))
            {
                requiresHostPlatform = () =>
                {
                    RedirectableConsole.ErrorWriteLine(
                        "WARNING: One or more projects requires host platforms to be generated, " +
                        "but the HostPlatformGeneration feature is not enabled.  Expect your " +
                        "build to fail.");
                };
            }

            // You can configure the default action for Protobuild in their project
            // with the <DefaultAction> tag in Module.xml.  If omitted, default to a resync.
            // Valid options for this tag are either "Generate", "Resync" or "Sync".

            // If the actions are "Resync" or "Sync", then we need to perform an initial
            // step against the primary platform.
            switch (action.ToLower())
            {
            case "generate":
                if (!this.GenerateProjectsForPlatform(
                        workingDirectory,
                        module,
                        primaryPlatform,
                        enabledServices,
                        disabledServices,
                        serviceSpecPath,
                        debugServiceResolution,
                        disablePackageResolution,
                        disableHostPlatformGeneration,
                        requiresHostPlatform,
                        debugProjectGeneration))
                {
                    return(false);
                }

                break;

            case "resync":
                if (!this.ResyncProjectsForPlatform(
                        workingDirectory,
                        module,
                        primaryPlatform,
                        enabledServices,
                        disabledServices,
                        serviceSpecPath,
                        debugServiceResolution,
                        disablePackageResolution,
                        disableHostPlatformGeneration,
                        requiresHostPlatform,
                        debugProjectGeneration))
                {
                    return(false);
                }

                break;

            case "sync":
                return(this.SyncProjectsForPlatform(module, primaryPlatform));

            case "clean":
                if (!this.CleanProjectsForPlatform(module, primaryPlatform))
                {
                    return(false);
                }

                break;

            default:
                RedirectableConsole.ErrorWriteLine("Unknown option in <DefaultAction> tag of Module.xml.  Defaulting to resync!");
                return(this.ResyncProjectsForPlatform(
                           workingDirectory,
                           module,
                           primaryPlatform,
                           enabledServices,
                           disabledServices,
                           serviceSpecPath,
                           debugServiceResolution,
                           disablePackageResolution,
                           disableHostPlatformGeneration,
                           requiresHostPlatform,
                           debugProjectGeneration));
            }

            // Now iterate through the multiple platforms specified.
            foreach (var platformIter in multiplePlatformsList.Distinct())
            {
                if (platformIter == primaryPlatform)
                {
                    // Already handled above.
                    continue;
                }

                // Resolve submodules as needed.
                if (!disablePackageResolution)
                {
                    this.m_PackageManager.ResolveAll(workingDirectory, module, platformIter, taskParallelisation, false, safeResolve);
                }

                switch (action.ToLower())
                {
                case "generate":
                case "resync":
                    // We do a generate under resync mode since we only want the primary platform
                    // to have synchronisation done (and it has had above).
                    if (!this.GenerateProjectsForPlatform(
                            workingDirectory,
                            module,
                            platformIter,
                            enabledServices,
                            disabledServices,
                            serviceSpecPath,
                            debugServiceResolution,
                            disablePackageResolution,
                            disableHostPlatformGeneration,
                            requiresHostPlatform,
                            debugProjectGeneration))
                    {
                        return(false);
                    }

                    break;

                case "clean":
                    if (!this.CleanProjectsForPlatform(module, platformIter))
                    {
                        return(false);
                    }

                    break;

                default:
                    throw new InvalidOperationException("Code should never reach this point");
                }
            }

            // If we implicitly require the host platform, generate that now (this variable can
            // only ever be set to true if the host platform is not already in the list of
            // platforms generated previously).
            if (implicitlyGenerateHostPlatform)
            {
                // Check to see if the host platform is supported.
                var hostPlatformNormalized = _moduleUtilities.NormalizePlatform(module, hostPlatform);
                if (hostPlatformNormalized == null)
                {
                    RedirectableConsole.WriteLine(
                        "WARNING: The current host platform is not a supported platform for the solution.  IDE editor " +
                        "projects and post-build hooks will not be available, and this may cause the project to be " +
                        "built incorrectly!");
                    return(true);
                }

                RedirectableConsole.WriteLine(
                    "One or more projects required the presence of host platform " +
                    "projects, implicitly starting generation for " + hostPlatform + "...");

                // Resolve submodules as needed.
                if (!disablePackageResolution)
                {
                    this.m_PackageManager.ResolveAll(workingDirectory, module, hostPlatform, taskParallelisation, false, safeResolve);
                }

                switch (action.ToLower())
                {
                case "generate":
                case "resync":
                    // We do a generate under resync mode since we only want the primary platform
                    // to have synchronisation done (and it has had above).
                    if (!this.GenerateProjectsForPlatform(
                            workingDirectory,
                            module,
                            hostPlatform,
                            enabledServices,
                            disabledServices,
                            serviceSpecPath,
                            debugServiceResolution,
                            disablePackageResolution,
                            disableHostPlatformGeneration,
                            requiresHostPlatform,
                            debugProjectGeneration))
                    {
                        return(false);
                    }

                    break;

                case "clean":
                    if (!this.CleanProjectsForPlatform(module, hostPlatform))
                    {
                        return(false);
                    }

                    break;

                default:
                    throw new InvalidOperationException("Code should never reach this point");
                }
            }

            // All the steps succeeded, so return true.
            return(true);
        }
        private void InstallVSIXIntoVisualStudio(string vsixPath, IKnownToolProvider knownToolProvider)
        {
            // This installation technique is for pre-2017 editions of Visual Studio.
            // We don't list 10.0 and 11.0 because they don't support all editions (you should
            // use GAC based installation for these editions instead).
            var vsVersions = new[]
            {
                "12.0",
                "14.0"
            };

            var editionRegistryKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)
                                     ?.OpenSubKey("SOFTWARE")
                                     ?.OpenSubKey("Microsoft")
                                     ?.OpenSubKey("VisualStudio");

            if (editionRegistryKey != null)
            {
                foreach (var version in vsVersions)
                {
                    var installPath = (string)editionRegistryKey?.OpenSubKey(version)?.GetValue("InstallDir");

                    if (installPath != null)
                    {
                        var vsixInstallerPath = Path.Combine(installPath, "VSIXInstaller.exe");

                        if (Directory.Exists(installPath))
                        {
                            if (File.Exists(vsixInstallerPath))
                            {
                                try
                                {
                                    RedirectableConsole.WriteLine("Installing VSIX into Visual Studio " + version + "...");
                                    var processStartInfo = new ProcessStartInfo();
                                    processStartInfo.FileName        = vsixInstallerPath;
                                    processStartInfo.Arguments       = "/q \"" + vsixPath + "\"";
                                    processStartInfo.UseShellExecute = false;
                                    var process = Process.Start(processStartInfo);
                                    process.WaitForExit();

                                    if (process.ExitCode != 0)
                                    {
                                        RedirectableConsole.ErrorWriteLine("VSIX installation failed for Visual Studio " + version + " (non-zero exit code)");
                                    }
                                    else
                                    {
                                        RedirectableConsole.WriteLine("VSIX installation completed successfully for Visual Studio " + version);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    RedirectableConsole.ErrorWriteLine("Failed to install VSIX for Visual Studio " + version + ": " + ex.Message);
                                }
                            }
                            else
                            {
                                RedirectableConsole.WriteLine("Visual Studio " + version + " does not provide VSIXInstaller.exe (checked for existance of " + vsixInstallerPath + ").");
                            }
                        }
                        else
                        {
                            RedirectableConsole.WriteLine("Visual Studio " + version + " is not installed (checked for existance of " + installPath + ").");
                        }
                    }
                }
            }

            // Now try and install in all editions of Visual Studio 2017 and later.  This
            // may install the vswhere global tool.
            var           vswhere       = knownToolProvider.GetToolExecutablePath("vswhere");
            List <string> installations = null;

            RedirectableConsole.WriteLine("Locating installations of Visual Studio 2017 and later...");
            try
            {
                var processStartInfo = new ProcessStartInfo();
                processStartInfo.FileName               = vswhere;
                processStartInfo.Arguments              = "-products * -property installationPath";
                processStartInfo.UseShellExecute        = false;
                processStartInfo.RedirectStandardOutput = true;
                var process             = Process.Start(processStartInfo);
                var installationsString = process.StandardOutput.ReadToEnd();
                installations = installationsString.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
                process.WaitForExit();

                if (process.ExitCode != 0)
                {
                    RedirectableConsole.ErrorWriteLine("Unable to locate Visual Studio 2017 and later installations (non-zero exit code from vswhere)");
                }
            }
            catch (Exception ex)
            {
                RedirectableConsole.ErrorWriteLine("Unable to locate Visual Studio 2017 and later installations: " + ex.Message);
            }

            if (installations != null)
            {
                foreach (var installPath in installations)
                {
                    var vsixInstallerPath = Path.Combine(installPath,
                                                         "Common7",
                                                         "IDE",
                                                         "VSIXInstaller.exe");

                    if (Directory.Exists(installPath))
                    {
                        if (File.Exists(vsixInstallerPath))
                        {
                            try
                            {
                                RedirectableConsole.WriteLine("Installing VSIX into " + installPath + "...");
                                var processStartInfo = new ProcessStartInfo();
                                processStartInfo.FileName        = vsixInstallerPath;
                                processStartInfo.Arguments       = "/q \"" + vsixPath + "\"";
                                processStartInfo.UseShellExecute = false;
                                var process = Process.Start(processStartInfo);
                                process.WaitForExit();

                                if (process.ExitCode != 0)
                                {
                                    RedirectableConsole.ErrorWriteLine("VSIX installation failed for " + installPath + " (non-zero exit code)");
                                }
                                else
                                {
                                    RedirectableConsole.WriteLine("VSIX installation completed successfully for " + installPath);
                                }
                            }
                            catch (Exception ex)
                            {
                                RedirectableConsole.ErrorWriteLine("Failed to install VSIX for " + installPath + ": " + ex.Message);
                            }
                        }
                        else
                        {
                            RedirectableConsole.WriteLine("Visual Studio at " + installPath + " does not provide VSIXInstaller.exe (checked for existance of " + vsixInstallerPath + ").");
                        }
                    }
                    else
                    {
                        RedirectableConsole.WriteLine("Visual Studio at " + installPath + " is not installed (checked for existance of " + installPath + ").");
                    }
                }
            }
        }
        private async Task <int> PushNuGetBinaryAsync(string targetUri, string apiKey, MemoryStream file, bool ignoreOnExisting)
        {
            byte[] fileBytes;
            fileBytes = new byte[(int)file.Length];
            file.Read(fileBytes, 0, fileBytes.Length);

            const string ApiKeyHeader = "X-NuGet-ApiKey";

            var boundary = Guid.NewGuid().ToString();

            byte[] boundaryBytes = Encoding.UTF8.GetBytes("--" + boundary + "\r\n");
            byte[] trailer       = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
            byte[] header        =
                Encoding.UTF8.GetBytes(
                    string.Format(
                        "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\";\r\nContent-Type: {2}\r\n\r\n",
                        "package", "package.nupkg",
                        "application/octet-stream"));

            var requestLength   = boundaryBytes.Length + header.Length + trailer.Length + fileBytes.Length;
            var combinedContent = new byte[requestLength];

            if (combinedContent == null)
            {
                throw new InvalidOperationException("Unable to create byte array " + requestLength + " bytes long for upload!");
            }

            boundaryBytes.CopyTo(combinedContent, 0);
            header.CopyTo(combinedContent, boundaryBytes.Length);
            fileBytes.CopyTo(combinedContent, boundaryBytes.Length + header.Length);
            trailer.CopyTo(combinedContent, boundaryBytes.Length + header.Length + fileBytes.Length);

            using (var client = new RetryableWebClient())
            {
                var       done   = false;
                byte[]    result = null;
                Exception ex     = null;
                var       uploadProgressRenderer = new UploadProgressRenderer();
                client.UploadDataCompleted += (sender, e) => {
                    if (e.Error != null)
                    {
                        ex = e.Error;
                    }

                    try
                    {
                        result = e.Result;
                    }
                    catch { }
                    done = true;
                };
                client.UploadProgressChanged += (sender, e) => {
                    if (!done)
                    {
                        uploadProgressRenderer.Update(e.ProgressPercentage, e.BytesSent / 1024);
                    }
                };

                client.SetHeader("Content-Type", "multipart/form-data; boundary=\"" + boundary + "\"");
                if (!string.IsNullOrWhiteSpace(apiKey))
                {
                    client.SetHeader(ApiKeyHeader, apiKey);
                }

                client.SetHeader("X-NuGet-Protocol-Version", "4.1.0");
                client.SetHeader("User-Agent", "Protobuild");

                client.UploadDataAsync(new Uri(targetUri), "PUT", combinedContent);
                while (!done)
                {
                    System.Threading.Thread.Sleep(0);
                }

                uploadProgressRenderer.FinalizeRendering();

                if (ex != null)
                {
                    var webException = ex as WebException;
                    if (webException != null)
                    {
                        var httpResponse = webException.Response as HttpWebResponse;
                        if (httpResponse != null)
                        {
                            RedirectableConsole.ErrorWriteLine(httpResponse.StatusDescription);

                            if (httpResponse.StatusCode == HttpStatusCode.Conflict)
                            {
                                if (ignoreOnExisting)
                                {
                                    // This is okay - the user doesn't care if there's an existing package with the same version.
                                    return(0);
                                }
                                else
                                {
                                    return(1);
                                }
                            }

                            return(1);
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException("Upload error", ex);
                    }
                }

                if (result != null)
                {
                    var resultDecoded = Encoding.UTF8.GetString(result);
                    if (!string.IsNullOrWhiteSpace(resultDecoded))
                    {
                        RedirectableConsole.WriteLine(Encoding.UTF8.GetString(result));
                    }
                    else
                    {
                        RedirectableConsole.WriteLine("Package uploaded successfully");
                    }
                }
                else
                {
                    RedirectableConsole.WriteLine("Package uploaded successfully");
                }

                return(0);
            }
        }
        public int Execute(Execution execution)
        {
            using (var client = new RetryableWebClient())
            {
                var sourcePackage = _packageUrlParser.Parse(execution.PackageUrl);

                RedirectableConsole.WriteLine("Retrieving source package...");
                var metadata = _packageLookup.Lookup(execution.WorkingDirectory, new PackageRequestRef(
                                                         sourcePackage.Uri,
                                                         sourcePackage.GitRef,
                                                         execution.PackagePushPlatform,
                                                         true,
                                                         sourcePackage.IsStaticReference));

                if (metadata.GetProtobuildPackageBinary == null)
                {
                    RedirectableConsole.ErrorWriteLine(
                        "ERROR: URL resolved to a package metadata type '" + metadata.GetType().Name + "', " +
                        "but this type doesn't provide a mechanism to convert to a Protobuild package.");
                }

                string archiveType;
                byte[] archiveData;
                metadata.GetProtobuildPackageBinary(metadata, out archiveType, out archiveData);

                var protobuildPackageMetadata = metadata as ProtobuildPackageMetadata;
                if (protobuildPackageMetadata == null)
                {
                    RedirectableConsole.ErrorWriteLine("--repush requires that the source URL resolve to a Protobuild package");
                    return(1);
                }

                RedirectableConsole.WriteLine("Detected package type as " + archiveType + ".");

                if (execution.PackagePushVersion.StartsWith("hash:", StringComparison.InvariantCulture))
                {
                    var sha1   = new SHA1Managed();
                    var hashed = sha1.ComputeHash(Encoding.ASCII.GetBytes(execution.PackagePushVersion.Substring("hash:".Length)));
                    execution.PackagePushVersion = BitConverter.ToString(hashed).ToLowerInvariant().Replace("-", "");
                }

                RedirectableConsole.WriteLine("Creating new package version...");

                var uploadParameters = new System.Collections.Specialized.NameValueCollection
                {
                    { "__apikey__", execution.PackagePushApiKey },
                    { "version", execution.PackagePushVersion },
                    { "platform", execution.PackagePushPlatform },
                };

                var json = fastJSON.JSON.ToDynamic(
                    System.Text.Encoding.ASCII.GetString(
                        client.UploadValues(execution.PackagePushUrl + "/version/new/api", uploadParameters)));

                if (json.has_error)
                {
                    RedirectableConsole.WriteLine(json.error);
                    return(1);
                }

                var uploadTarget   = (string)json.result.uploadUrl;
                var finalizeTarget = (string)json.result.finalizeUrl;

                RedirectableConsole.WriteLine("Uploading package...");
                this.PushBinary(uploadTarget, archiveData);

                RedirectableConsole.WriteLine("Finalizing package version...");

                var finalizeParameters = new System.Collections.Specialized.NameValueCollection
                {
                    { "__apikey__", execution.PackagePushApiKey },
                    { "archiveType", archiveType },
                };

                json = fastJSON.JSON.ToDynamic(
                    System.Text.Encoding.ASCII.GetString(
                        client.UploadValues(finalizeTarget, finalizeParameters)));

                if (json.has_error)
                {
                    RedirectableConsole.WriteLine(json.error);
                    return(1);
                }

                if (execution.PackagePushBranchToUpdate != null)
                {
                    RedirectableConsole.WriteLine("Updating branch " + execution.PackagePushBranchToUpdate + " to point at new version...");

                    var branchUpdateParameters = new System.Collections.Specialized.NameValueCollection
                    {
                        { "__apikey__", execution.PackagePushApiKey },
                        { "name", execution.PackagePushBranchToUpdate },
                        { "git", execution.PackagePushVersion },
                    };

                    json = fastJSON.JSON.ToDynamic(
                        System.Text.Encoding.ASCII.GetString(
                            client.UploadValues(
                                execution.PackagePushUrl + "/branch/edit/" + execution.PackagePushBranchToUpdate + "/api",
                                branchUpdateParameters)));

                    if (json.has_error)
                    {
                        RedirectableConsole.WriteLine(json.error);
                        return(1);
                    }
                }

                RedirectableConsole.WriteLine("Package version repushed successfully.");
            }
            return(0);
        }
Beispiel #24
0
        private static ModuleInfo LoadInternal(XDocument doc, string modulePath, Func <ModuleInfo> fallback)
        {
            var def = new ModuleInfo();
            var xsi = doc.Root == null ? null : doc.Root.Attribute(XName.Get("xsi", "http://www.w3.org/2000/xmlns/"));

            if (xsi != null && xsi.Value == "http://www.w3.org/2001/XMLSchema-instance")
            {
                return(fallback());
            }

            Func <string, string> getStringValue = name =>
            {
                if (doc.Root == null)
                {
                    return(null);
                }

                var elem = doc.Root.Element(XName.Get(name));
                if (elem == null)
                {
                    return(null);
                }

                return(elem.Value);
            };

            def.Name                      = getStringValue("Name");
            def.Authors                   = getStringValue("Authors");
            def.Description               = getStringValue("Description");
            def.ProjectUrl                = getStringValue("ProjectUrl");
            def.LicenseUrl                = getStringValue("LicenseUrl");
            def.IconUrl                   = getStringValue("IconUrl");
            def.GitRepositoryUrl          = getStringValue("GitRepositoryUrl");
            def.SemanticVersion           = getStringValue("SemanticVersion");
            def.Path                      = modulePath;
            def.DefaultAction             = getStringValue("DefaultAction");
            def.DefaultLinuxPlatforms     = getStringValue("DefaultLinuxPlatforms");
            def.DefaultMacOSPlatforms     = getStringValue("DefaultMacOSPlatforms");
            def.DefaultWindowsPlatforms   = getStringValue("DefaultWindowsPlatforms");
            def.DefaultStartupProject     = getStringValue("DefaultStartupProject");
            def.SupportedPlatforms        = getStringValue("SupportedPlatforms");
            def.DisableSynchronisation    = getStringValue("DisableSynchronisation") == "true";
            def.GenerateNuGetRepositories = getStringValue("GenerateNuGetRepositories") == "true";
            def.Packages                  = new List <PackageRef>();

            if (doc.Root != null)
            {
                var packagesElem = doc.Root.Element(XName.Get("Packages"));
                if (packagesElem != null)
                {
                    var packages = packagesElem.Elements();
                    foreach (var package in packages)
                    {
                        var folder = package.Attribute(XName.Get("Folder"))?.Value;
                        var gitRef = package.Attribute(XName.Get("GitRef"))?.Value;
                        var uri    = package.Attribute(XName.Get("Uri"))?.Value;

                        var repository  = package.Attribute(XName.Get("Repository"))?.Value;
                        var packageName = package.Attribute(XName.Get("Package"))?.Value;
                        var version     = package.Attribute(XName.Get("Version"))?.Value;

                        if (!string.IsNullOrWhiteSpace(repository) && !string.IsNullOrWhiteSpace(packageName) && !string.IsNullOrWhiteSpace(version))
                        {
                            // This is NuGet v3 package.  Rather than writing a different interface,
                            // we just automatically form a URI that complies with the rest of the
                            // package resolution system.
                            uri = repository;
                            if (!string.IsNullOrWhiteSpace(uri))
                            {
                                uri  = uri.Replace("https://", "https-nuget-v3://");
                                uri  = uri.Replace("http://", "http-nuget-v3://");
                                uri += $"|{packageName}";
                            }

                            gitRef = version;
                        }

                        if (string.IsNullOrWhiteSpace(folder) && !string.IsNullOrWhiteSpace(packageName))
                        {
                            folder = packageName;
                        }

                        if (string.IsNullOrWhiteSpace(folder) || string.IsNullOrWhiteSpace(gitRef) ||
                            string.IsNullOrWhiteSpace(uri))
                        {
                            RedirectableConsole.ErrorWriteLine("WARNING: Invalid package declaration in module; skipping package.");
                            continue;
                        }

                        var packageRef = new PackageRef
                        {
                            Folder    = folder,
                            GitRef    = gitRef,
                            Uri       = uri,
                            Platforms = null,
                        };

                        var platforms      = package.Attribute(XName.Get("Platforms"));
                        var platformsArray = platforms?.Value.Split(',');
                        if (platformsArray?.Length > 0)
                        {
                            packageRef.Platforms = platformsArray;
                        }

                        def.Packages.Add(packageRef);
                    }
                }

                var featureSetElem = doc.Root.Element(XName.Get("FeatureSet"));
                if (featureSetElem != null)
                {
                    def.FeatureSet = new List <Feature>();

                    var features = featureSetElem.Elements();
                    foreach (var feature in features)
                    {
                        try
                        {
                            def.FeatureSet.Add((Feature)Enum.Parse(typeof(Feature), feature.Value));
                        }
                        catch
                        {
                            RedirectableConsole.ErrorWriteLine("Unknown feature in Module.xml; ignoring: " + feature.Value);
                        }
                    }
                }
                else
                {
                    def.FeatureSet = null;
                }

                // Check if the feature set is present and if it does not contain
                // the PackageManagement feature.  If that feature isn't there, we
                // ignore any of the data in Packages and just set the value to
                // an empty list.
                if (def.FeatureSet != null && !def.FeatureSet.Contains(Feature.PackageManagement))
                {
                    def.Packages.Clear();
                }
            }

            return(def);
        }