public bool ExecuteLaunchCtlCommand(string[] arguments, int checkInterval = 600, string workingDirectory = null)
        {
            var binary  = arguments.FirstOrDefault();
            var options = new StartOptions
            {
                Id       = Process.GetCurrentProcess().Id.ToString(),
                DateTime = DateTime.Now
            };
            var labelName = $"com.xamarin.nativebuild.tasks.{options.GetFormattedDateTime()}.{options.Id}.{Path.GetFileNameWithoutExtension(binary)}".ToLowerInvariant();
            var root      = $"/tmp/{labelName}";

            var outputLog = CrossPath.CombineSsh(root, "output.log");
            var errorLog  = CrossPath.CombineSsh(root, "error.log");
            var appPList  = CrossPath.CombineSsh(root, "app.plist");

            // upload the plist
            var plist = CreatePList(labelName, outputLog, errorLog, arguments, workingDirectory);

            CreateDirectory(root);
            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(plist)))
            {
                CreateFile(stream, appPList);
            }

            // create the logs and start the process
            var launchctl = ExecuteCommand($"touch {outputLog}; touch {errorLog}; launchctl load -S Aqua {appPList}");

            if (!WasSuccess(launchctl))
            {
                Log.LogError($"Unable to starting {binary}: {launchctl.Result}");
                return(false);
            }

            // tail and stream the output
            var tailOutput = ExecuteCommandStream($"tail -f {outputLog} & while launchctl list {labelName} &> /dev/null; do sleep {checkInterval / 1000}; done; kill $!;");

            if (!WasSuccess(tailOutput))
            {
                Log.LogError($"There was an error reading the output: {tailOutput.Result}");
                return(false);
            }

            // get the errors
            var tailError = ExecuteCommand($"cat {errorLog}");

            if (!WasSuccess(tailError))
            {
                Log.LogError($"There was an error reading the error log: {tailError.Result}");
                return(false);
            }
            else if (!string.IsNullOrEmpty(tailError.Result))
            {
                Log.LogError($"There was an error: {tailError.Result}");
                return(false);
            }

            return(true);
        }
        public bool CreatePodfileXCodeProject(string podfileRoot, Podfile podfile, bool?noRepoUpdate = null)
        {
            if (CancellationToken.IsCancellationRequested)
            {
                Log.LogError("Task was canceled.");
                return(false);
            }

            var podfilePath     = CrossPath.CombineSsh(podfileRoot, "Podfile");
            var podfileLockPath = CrossPath.CombineSsh(podfileRoot, "Podfile.lock");

            // see if we can avoid updating the master repo
            noRepoUpdate = noRepoUpdate == true || (noRepoUpdate == null && Ssh.FileExists(podfileLockPath));

            // create and restore a Podfile
            return(CreatePodfile(podfile, podfilePath) && RestorePodfile(podfileRoot, noRepoUpdate));
        }
示例#3
0
        private XCodeBuildOutputs BuildPodfileXCodeProject(string podfileRoot, string[] targets, XCodeArchitectures architectures, bool framework)
        {
            var parameters = new XCodeBuildParameters
            {
                ArchitectureSettings = XCodeBuildArchitecture,
                ArtifactsDirectory   = CrossPath.CombineSsh(podfileRoot, "build"),
                OutputDirectory      = CrossPath.CombineSsh(podfileRoot, "out"),
                IsFrameworks         = framework,
                ProjectFilePath      = CrossPath.CombineSsh(podfileRoot, "Pods/Pods.xcodeproj"),
                BuildTargets         = new[] { "Pods-MSBuildTask" },
                OutputTargets        = targets,
                ArchitectureOverride = architectures
            };
            var outputs = new XCodeBuildOutputs();

            if (XCodeBuild.BuildXCodeProject(parameters, outputs))
            {
                return(outputs);
            }
            else
            {
                return(null);
            }
        }
示例#4
0
        public bool BuildXCodeProject(XCodeBuildParameters parameters, XCodeBuildOutputs outputs)
        {
            outputs.ProjectDirectory   = CrossPath.GetDirectoryNameSsh(parameters.ProjectFilePath);
            outputs.OutputDirectory    = parameters.OutputDirectory ?? CrossPath.CombineSsh(outputs.ProjectDirectory, "out");
            outputs.ArtifactsDirectory = parameters.ArtifactsDirectory ?? CrossPath.CombineSsh(outputs.ProjectDirectory, "build");

            foreach (var arch in parameters.SplitArchitectures)
            {
                if (CancellationToken.IsCancellationRequested)
                {
                    Log.LogError("Task was canceled.");
                    return(false);
                }

                var artifactsPath = CrossPath.CombineSsh(outputs.ArtifactsDirectory, parameters.ArchitectureSettings.GetArtifactDirectoryName("Release", arch: arch));

                // build the project
                var result = Ssh.ExecuteLaunchCtlCommand(
                    workingDirectory: outputs.ProjectDirectory,
                    arguments: new[]
                {
                    XCodeBuildToolPath,
                    "-project", parameters.ProjectFilePath,
                    "-target", parameters.BuildTargets[0],     // TODO
                    "-configuration", "Release",
                    "-arch", arch.ToString().ToLowerInvariant(),
                    "-sdk", parameters.ArchitectureSettings.GetSdk(arch).ToString().ToLowerInvariant(),
                    "build"
                });
                if (!result)
                {
                    Log.LogError($"Error building the XCode project.");
                    return(false);
                }

                if (CancellationToken.IsCancellationRequested)
                {
                    Log.LogError("Task was canceled.");
                    return(false);
                }

                // copy the artifacts to the intermediates directory, naming them per architecture
                foreach (var target in parameters.OutputTargets)
                {
                    // this might be set multiple times as this is target-based,
                    // but we are building the same target multiple times for each arch
                    outputs[target].IntermediateDirectory = CrossPath.CombineSsh(outputs.OutputDirectory, target, "obj");
                    Ssh.CreateDirectory(outputs[target].IntermediateDirectory);

                    var currentIntermediate = outputs[target].Intermediates[arch];
                    currentIntermediate.IsFrameworks = parameters.IsFrameworks;

                    if (parameters.IsFrameworks)
                    {
                        var cleanTarget = CleanFrameworkName(target);
                        currentIntermediate.Path = CrossPath.CombineSsh(outputs[target].IntermediateDirectory, $"{cleanTarget}-{arch}.framework");
                        Ssh.CopyPath(CrossPath.CombineSsh(artifactsPath, $"{cleanTarget}.framework"), currentIntermediate.Path);
                    }
                    else
                    {
                        currentIntermediate.Path = CrossPath.CombineSsh(outputs.OutputDirectory, target, "obj", $"lib{target}-{arch}.a");
                        Ssh.CopyPath(CrossPath.CombineSsh(artifactsPath, $"lib{target}.a"), currentIntermediate.Path);
                    }
                }
            }

            // run lipo on the outputs, from the obj to the out
            foreach (var targetOutput in outputs)
            {
                targetOutput.Directory = CrossPath.CombineSsh(parameters.OutputDirectory, targetOutput.Target);

                // lipo the .a
                var staticIntermediates = targetOutput.Intermediates.Where(i => !i.IsFrameworks);
                if (staticIntermediates.Any())
                {
                    targetOutput.ArchiveOutput = new BuildArchitectureOutput(targetOutput)
                    {
                        Architecture = Utilities.CreateEnum(staticIntermediates.Select(i => i.Architecture)),
                        IsFrameworks = false,
                        Path         = CrossPath.CombineSsh(parameters.OutputDirectory, targetOutput.Target, $"lib{targetOutput.Target}.a")
                    };
                    if (!RunLipo(targetOutput.ArchiveOutput.Path, staticIntermediates.Select(i => i.Path)))
                    {
                        return(false);
                    }
                }

                // lipo the .framework
                var frameworkIntermediates = targetOutput.Intermediates.Where(i => i.IsFrameworks);
                if (frameworkIntermediates.Any())
                {
                    var cleanTarget = CleanFrameworkName(targetOutput.Target);

                    targetOutput.FrameworkOutput = new BuildArchitectureOutput(targetOutput)
                    {
                        Architecture = Utilities.CreateEnum(frameworkIntermediates.Select(i => i.Architecture)),
                        IsFrameworks = true,
                        Path         = CrossPath.CombineSsh(parameters.OutputDirectory, targetOutput.Target, $"{cleanTarget}.framework")
                    };

                    // copy the first arch as we need the other files
                    var firstArch = frameworkIntermediates.First();
                    Ssh.CopyPath(firstArch.Path, targetOutput.FrameworkOutput.Path);

                    // now lipo the archive
                    if (!RunLipo(CrossPath.CombineSsh(targetOutput.FrameworkOutput.Path, cleanTarget), frameworkIntermediates.Select(i => CrossPath.CombineSsh(i.Path, cleanTarget))))
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }