Exemplo n.º 1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="ProjectFile"></param>
        /// <param name="Executable"></param>
        /// <param name="StageDirectory"></param>
        /// <param name="PlatformType"></param>
        public static void GenerateAssetCatalog(FileReference ProjectFile, string Executable, string StageDirectory, UnrealTargetPlatform PlatformType)
        {
            // Initialize the toolchain.
            IOSProjectSettings ProjectSettings = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(PlatformType)).ReadProjectSettings(null);
            IOSToolChain       ToolChain       = new IOSToolChain(ProjectFile, ProjectSettings);

            // Determine whether the user has modified icons that require a remote Mac to build.
            CppPlatform Platform         = PlatformType == UnrealTargetPlatform.IOS ? CppPlatform.IOS : CppPlatform.TVOS;
            bool        bUserImagesExist = false;

            ToolChain.GenerateAssetCatalog(Platform, ref bUserImagesExist);

            // Don't attempt to do anything remotely if the user is using the default UE4 images.
            if (!bUserImagesExist)
            {
                return;
            }

            // Also don't attempt to use a remote Mac if packaging for TVOS on PC.
            if (Platform == CppPlatform.TVOS && BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac)
            {
                return;
            }

            // Save off the current bUseRPCUtil setting to restore at the end of this function.
            // At this time, iPhonePackager needs to be called with bUseRPCUtil == true.
            bool bSaveUseRPCUtil = RemoteToolChain.bUseRPCUtil;

            // Initialize the remote calling environment, taking into account the user's SSH setting.
            ToolChain.SetUpGlobalEnvironment(false);

            // Build the asset catalog ActionGraph.
            ActionGraph     ActionGraph = new ActionGraph();
            List <FileItem> OutputFiles = new List <FileItem>();

            ToolChain.CompileAssetCatalog(FileItem.GetItemByPath(Executable), Platform, ActionGraph, OutputFiles);

            ActionGraph.FinalizeActionGraph();

            // I'm not sure how to derive the UE4Game and Development arguments programmatically.
            string[] Arguments = new string[] { "UE4Game", (PlatformType == UnrealTargetPlatform.IOS ? "IOS" : "TVOS"), "Development", "-UniqueBuildEnvironment" };

            // Perform all of the setup necessary to actually execute the ActionGraph instance.
            ReadOnlyBuildVersion Version        = new ReadOnlyBuildVersion(BuildVersion.ReadDefault());
            List <string[]>      TargetSettings = new List <string[]>();

            TargetSettings.Add(Arguments);
            var Targets = new List <UEBuildTarget>();
            Dictionary <UEBuildTarget, CPPHeaders> TargetToHeaders = new Dictionary <UEBuildTarget, CPPHeaders>();
            List <TargetDescriptor> TargetDescs = new List <TargetDescriptor>();

            foreach (string[] TargetSetting in TargetSettings)
            {
                TargetDescs.AddRange(TargetDescriptor.ParseCommandLine(TargetSetting, ref ProjectFile));
            }
            foreach (TargetDescriptor TargetDesc in TargetDescs)
            {
                UEBuildTarget Target = UEBuildTarget.CreateTarget(TargetDesc, Arguments, false, Version);
                if (Target == null)
                {
                    continue;
                }
                Targets.Add(Target);
                TargetToHeaders.Add(Target, null);
            }

            bool bIsRemoteCompile = BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac;

            // Create the build configuration object, and read the settings
            BuildConfiguration BuildConfiguration = new BuildConfiguration();

            XmlConfig.ApplyTo(BuildConfiguration);
            CommandLine.ParseArguments(Arguments, BuildConfiguration);
            BuildConfiguration.bUseUBTMakefiles = false;

            Action[] PrerequisiteActions;
            {
                HashSet <Action> PrerequisiteActionsSet = new HashSet <Action>();
                foreach (FileItem OutputFile in OutputFiles)
                {
                    ActionGraph.GatherPrerequisiteActions(OutputFile, ref PrerequisiteActionsSet);
                }
                PrerequisiteActions = PrerequisiteActionsSet.ToArray();
            }

            // Copy any asset catalog files to the remote Mac, if necessary.
            foreach (UEBuildTarget Target in Targets)
            {
                UEBuildPlatform.GetBuildPlatform(Target.Platform).PreBuildSync();
            }

            // Begin execution of the ActionGraph.
            Dictionary <UEBuildTarget, List <FileItem> > TargetToOutdatedPrerequisitesMap;
            List <Action> ActionsToExecute = ActionGraph.GetActionsToExecute(BuildConfiguration, PrerequisiteActions, Targets, TargetToHeaders, true, true, out TargetToOutdatedPrerequisitesMap);
            string        ExecutorName     = "Unknown";
            bool          bSuccess         = ActionGraph.ExecuteActions(BuildConfiguration, ActionsToExecute, bIsRemoteCompile, out ExecutorName, "", EHotReload.Disabled);

            if (bSuccess)
            {
                if (bIsRemoteCompile)
                {
                    // Copy the remotely built AssetCatalog directory locally.
                    foreach (FileItem OutputFile in OutputFiles)
                    {
                        string   RemoteDirectory = System.IO.Path.GetDirectoryName(OutputFile.AbsolutePath).Replace("\\", "/");
                        FileItem LocalExecutable = ToolChain.RemoteToLocalFileItem(FileItem.GetItemByPath(Executable));
                        string   LocalDirectory  = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(LocalExecutable.AbsolutePath), "AssetCatalog");
                        LocalDirectory = StageDirectory;
                        RPCUtilHelper.CopyDirectory(RemoteDirectory, LocalDirectory, RPCUtilHelper.ECopyOptions.DoNotReplace);
                    }
                }
                else
                {
                    // Copy the built AssetCatalog directory to the StageDirectory.
                    foreach (FileItem OutputFile in OutputFiles)
                    {
                        string SourceDirectory = System.IO.Path.GetDirectoryName(OutputFile.AbsolutePath).Replace("\\", "/");
                        System.IO.DirectoryInfo SourceDirectoryInfo = new System.IO.DirectoryInfo(SourceDirectory);
                        if (!System.IO.Directory.Exists(StageDirectory))
                        {
                            System.IO.Directory.CreateDirectory(StageDirectory);
                        }
                        System.IO.FileInfo[] SourceFiles = SourceDirectoryInfo.GetFiles();
                        foreach (System.IO.FileInfo SourceFile in SourceFiles)
                        {
                            string DestinationPath = System.IO.Path.Combine(StageDirectory, SourceFile.Name);
                            SourceFile.CopyTo(DestinationPath, true);
                        }
                    }
                }
            }

            // Restore the former bUseRPCUtil setting.
            RemoteToolChain.bUseRPCUtil = bSaveUseRPCUtil;
        }
Exemplo n.º 2
0
        /// <summary>
        /// Parse a list of target descriptors from the command line
        /// </summary>
        /// <param name="Arguments">Command-line arguments</param>
        /// <param name="ProjectFile">The project file, if already set. May be updated if not.</param>
        /// <returns>List of target descriptors</returns>
        public static List <TargetDescriptor> ParseCommandLine(string[] Arguments, ref FileReference ProjectFile)
        {
            UnrealTargetPlatform      Platform      = UnrealTargetPlatform.Unknown;
            UnrealTargetConfiguration Configuration = UnrealTargetConfiguration.Unknown;
            List <string>             TargetNames   = new List <string>();
            List <TargetType>         TargetTypes   = new List <TargetType>();
            string            Architecture          = null;
            List <OnlyModule> OnlyModules           = new List <OnlyModule>();
            FileReference     ForeignPlugin         = null;
            string            ForceReceiptFileName  = null;

            // Settings for creating/using static libraries for the engine
            for (int ArgumentIndex = 0; ArgumentIndex < Arguments.Length; ArgumentIndex++)
            {
                string Argument = Arguments[ArgumentIndex];
                if (!Argument.StartsWith("-"))
                {
                    UnrealTargetPlatform ParsedPlatform;
                    if (Enum.TryParse(Argument, true, out ParsedPlatform) && ParsedPlatform != UnrealTargetPlatform.Unknown)
                    {
                        if (Platform != UnrealTargetPlatform.Unknown)
                        {
                            throw new BuildException("Multiple platforms specified on command line (first {0}, then {1})", Platform, ParsedPlatform);
                        }
                        Platform = ParsedPlatform;
                        continue;
                    }

                    UnrealTargetConfiguration ParsedConfiguration;
                    if (Enum.TryParse(Argument, true, out ParsedConfiguration) && ParsedConfiguration != UnrealTargetConfiguration.Unknown)
                    {
                        if (Configuration != UnrealTargetConfiguration.Unknown)
                        {
                            throw new BuildException("Multiple configurations specified on command line (first {0}, then {1})", Configuration, ParsedConfiguration);
                        }
                        Configuration = ParsedConfiguration;
                        continue;
                    }

                    // Make sure the target name is valid. It may be the path to a project file.
                    if (Argument.IndexOfAny(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, '.' }) == -1)
                    {
                        TargetNames.Add(Argument);
                    }
                }
                else
                {
                    string Value;
                    if (ParseArgumentValue(Argument, "-TargetType=", out Value))
                    {
                        TargetType Type;
                        if (!Enum.TryParse(Value, true, out Type))
                        {
                            throw new BuildException("Invalid target type: '{0}'", Value);
                        }
                        TargetTypes.Add(Type);
                    }
                    else if (ParseArgumentValue(Argument, "-Module=", out Value))
                    {
                        OnlyModules.Add(new OnlyModule(Value));
                    }
                    else if (ParseArgumentValue(Argument, "-ModuleWithSuffix=", out Value))
                    {
                        int SuffixIdx = Value.LastIndexOf(',');
                        if (SuffixIdx == -1)
                        {
                            throw new BuildException("Missing suffix argument from -ModuleWithSuffix=Name,Suffix");
                        }
                        OnlyModules.Add(new OnlyModule(Value.Substring(0, SuffixIdx), Value.Substring(SuffixIdx + 1)));
                    }
                    else if (ParseArgumentValue(Argument, "-Plugin=", out Value))
                    {
                        if (ForeignPlugin != null)
                        {
                            throw new BuildException("Only one foreign plugin to compile may be specified per invocation");
                        }
                        ForeignPlugin = new FileReference(Value);
                    }
                    else if (ParseArgumentValue(Argument, "-Receipt=", out Value))
                    {
                        ForceReceiptFileName = Value;
                    }
                    else
                    {
                        switch (Arguments[ArgumentIndex].ToUpperInvariant())
                        {
                        case "-MODULE":
                            throw new BuildException("'-Module <Name>' syntax is no longer supported on the command line. Use '-Module=<Name>' instead.");

                        case "-MODULEWITHSUFFIX":
                            throw new BuildException("'-ModuleWithSuffix <Name> <Suffix>' syntax is no longer supported on the command line. Use '-Module=<Name>,<Suffix>' instead.");

                        case "-PLUGIN":
                            throw new BuildException("'-Plugin <Path>' syntax is no longer supported on the command line. Use '-Plugin=<Path>' instead.");

                        case "-RECEIPT":
                            throw new BuildException("'-Receipt <Path>' syntax is no longer supported on the command line. Use '-Receipt=<Path>' instead.");
                        }
                    }
                }
            }

            if (Platform == UnrealTargetPlatform.Unknown)
            {
                throw new BuildException("Couldn't find platform name.");
            }
            if (Configuration == UnrealTargetConfiguration.Unknown)
            {
                throw new BuildException("Couldn't determine configuration name.");
            }

            if (Architecture == null)
            {
                Architecture = UEBuildPlatform.GetBuildPlatform(Platform).GetDefaultArchitecture(ProjectFile);
            }

            // Create all the target descriptors for targets specified by type
            foreach (TargetType Type in TargetTypes)
            {
                if (ProjectFile == null)
                {
                    throw new BuildException("-TargetType=... requires a project file to be specified");
                }
                else
                {
                    TargetNames.Add(RulesCompiler.CreateProjectRulesAssembly(ProjectFile).GetTargetNameByType(Type, Platform, Configuration, Architecture, ProjectFile, new ReadOnlyBuildVersion(BuildVersion.ReadDefault())));
                }
            }

            // Create all the target descriptor
            List <TargetDescriptor> Targets = new List <TargetDescriptor>();

            foreach (string TargetName in TargetNames)
            {
                // If a project file was not specified see if we can find one
                if (ProjectFile == null && UProjectInfo.TryGetProjectForTarget(TargetName, out ProjectFile))
                {
                    Log.TraceVerbose("Found project file for {0} - {1}", TargetName, ProjectFile);
                }

                TargetDescriptor Target = new TargetDescriptor(ProjectFile, TargetName, Platform, Configuration, Architecture);
                Target.OnlyModules          = OnlyModules;
                Target.ForeignPlugin        = ForeignPlugin;
                Target.ForceReceiptFileName = ForceReceiptFileName;
                Targets.Add(Target);
            }

            // Make sure we could parse something
            if (Targets.Count == 0)
            {
                throw new BuildException("No target name was specified on the command-line.");
            }
            return(Targets);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Build a target remotely
        /// </summary>
        /// <param name="TargetDesc">Descriptor for the target to build</param>
        /// <param name="RemoteLogFile">Path to store the remote log file</param>
        /// <returns>True if the build succeeded, false otherwise</returns>
        public bool Build(TargetDescriptor TargetDesc, FileReference RemoteLogFile)
        {
            // Get the directory for working files
            DirectoryReference BaseDir = DirectoryReference.FromFile(TargetDesc.ProjectFile) ?? UnrealBuildTool.EngineDirectory;
            DirectoryReference TempDir = DirectoryReference.Combine(BaseDir, "Intermediate", "Remote", TargetDesc.Name, TargetDesc.Platform.ToString(), TargetDesc.Configuration.ToString());

            DirectoryReference.CreateDirectory(TempDir);

            bool bLogIsMapped = false;

            foreach (RemoteMapping Mapping in Mappings)
            {
                if (RemoteLogFile.Directory.FullName.Equals(Mapping.LocalDirectory.FullName, StringComparison.InvariantCultureIgnoreCase))
                {
                    bLogIsMapped = true;
                    break;
                }
            }
            if (!bLogIsMapped)
            {
                Mappings.Add(new RemoteMapping(RemoteLogFile.Directory, GetRemotePath(RemoteLogFile.Directory)));
            }

            // Compile the rules assembly
            RulesAssembly RulesAssembly = RulesCompiler.CreateTargetRulesAssembly(TargetDesc.ProjectFile, TargetDesc.Name, false, false, TargetDesc.ForeignPlugin);

            // Create the target rules
            TargetRules Rules = RulesAssembly.CreateTargetRules(TargetDesc.Name, TargetDesc.Platform, TargetDesc.Configuration, TargetDesc.Architecture, TargetDesc.ProjectFile, new ReadOnlyBuildVersion(BuildVersion.ReadDefault()), new string[0]);

            // Check if we need to enable a nativized plugin, and compile the assembly for that if we do
            FileReference NativizedPluginFile = Rules.GetNativizedPlugin();

            if (NativizedPluginFile != null)
            {
                RulesAssembly = RulesCompiler.CreatePluginRulesAssembly(NativizedPluginFile, false, RulesAssembly, false);
            }

            // Path to the local manifest file. This has to be translated from the remote format after the build is complete.
            List <FileReference> LocalManifestFiles = new List <FileReference>();

            // Path to the remote manifest file
            FileReference RemoteManifestFile = FileReference.Combine(TempDir, "Manifest.xml");

            // Prepare the arguments we will pass to the remote build
            List <string> RemoteArguments = new List <string>();

            RemoteArguments.Add(TargetDesc.Name);
            RemoteArguments.Add(TargetDesc.Platform.ToString());
            RemoteArguments.Add(TargetDesc.Configuration.ToString());
            RemoteArguments.Add("-SkipRulesCompile");                                                      // Use the rules assembly built locally
            RemoteArguments.Add(String.Format("-XmlConfigCache={0}", GetRemotePath(XmlConfig.CacheFile))); // Use the XML config cache built locally, since the remote won't have it
            RemoteArguments.Add(String.Format("-Log={0}", GetRemotePath(RemoteLogFile)));
            RemoteArguments.Add(String.Format("-Manifest={0}", GetRemotePath(RemoteManifestFile)));

            if (TargetDesc.ProjectFile != null)
            {
                RemoteArguments.Add(String.Format("-Project={0}", GetRemotePath(TargetDesc.ProjectFile)));
            }

            foreach (string LocalArgument in TargetDesc.AdditionalArguments)
            {
                int EqualsIdx = LocalArgument.IndexOf('=');
                if (EqualsIdx == -1)
                {
                    RemoteArguments.Add(LocalArgument);
                    continue;
                }

                string Key   = LocalArgument.Substring(0, EqualsIdx);
                string Value = LocalArgument.Substring(EqualsIdx + 1);

                if (Key.Equals("-Log", StringComparison.InvariantCultureIgnoreCase))
                {
                    // We are already writing to the local log file. The remote will produce a different log (RemoteLogFile)
                    continue;
                }
                if (Key.Equals("-Manifest", StringComparison.InvariantCultureIgnoreCase))
                {
                    LocalManifestFiles.Add(new FileReference(Value));
                    continue;
                }

                string RemoteArgument = LocalArgument;
                foreach (RemoteMapping Mapping in Mappings)
                {
                    if (Value.StartsWith(Mapping.LocalDirectory.FullName, StringComparison.InvariantCultureIgnoreCase))
                    {
                        RemoteArgument = String.Format("{0}={1}", Key, GetRemotePath(Value));
                        break;
                    }
                }
                RemoteArguments.Add(RemoteArgument);
            }

            // Handle any per-platform setup that is required
            if (TargetDesc.Platform == UnrealTargetPlatform.IOS || TargetDesc.Platform == UnrealTargetPlatform.TVOS)
            {
                // Always generate a .stub
                RemoteArguments.Add("-CreateStub");

                // Cannot use makefiles, since we need PostBuildSync() to generate the IPA (and that requires a TargetRules instance)
                RemoteArguments.Add("-NoUBTMakefiles");

                // Get the provisioning data for this project
                IOSProvisioningData ProvisioningData = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(TargetDesc.Platform)).ReadProvisioningData(TargetDesc.ProjectFile);
                if (ProvisioningData == null || ProvisioningData.MobileProvisionFile == null)
                {
                    throw new BuildException("Unable to find mobile provision for {0}. See log for more information.", TargetDesc.Name);
                }

                // Create a local copy of the provision
                FileReference MobileProvisionFile = FileReference.Combine(TempDir, ProvisioningData.MobileProvisionFile.GetFileName());
                if (FileReference.Exists(MobileProvisionFile))
                {
                    FileReference.SetAttributes(MobileProvisionFile, FileAttributes.Normal);
                }
                FileReference.Copy(ProvisioningData.MobileProvisionFile, MobileProvisionFile, true);
                Log.TraceInformation("[Remote] Uploading {0}", MobileProvisionFile);
                UploadFile(MobileProvisionFile);

                // Extract the certificate for the project. Try to avoid calling IPP if we already have it.
                FileReference CertificateFile = FileReference.Combine(TempDir, "Certificate.p12");

                FileReference CertificateInfoFile     = FileReference.Combine(TempDir, "Certificate.txt");
                string        CertificateInfoContents = String.Format("{0}\n{1}", ProvisioningData.MobileProvisionFile, FileReference.GetLastWriteTimeUtc(ProvisioningData.MobileProvisionFile).Ticks);

                if (!FileReference.Exists(CertificateFile) || !FileReference.Exists(CertificateInfoFile) || FileReference.ReadAllText(CertificateInfoFile) != CertificateInfoContents)
                {
                    Log.TraceInformation("[Remote] Exporting certificate for {0}...", ProvisioningData.MobileProvisionFile);

                    StringBuilder Arguments = new StringBuilder("ExportCertificate");
                    if (TargetDesc.ProjectFile == null)
                    {
                        Arguments.AppendFormat(" \"{0}\"", UnrealBuildTool.EngineSourceDirectory);
                    }
                    else
                    {
                        Arguments.AppendFormat(" \"{0}\"", TargetDesc.ProjectFile.Directory);
                    }
                    Arguments.AppendFormat(" -provisionfile \"{0}\"", ProvisioningData.MobileProvisionFile);
                    Arguments.AppendFormat(" -outputcertificate \"{0}\"", CertificateFile);
                    if (TargetDesc.Platform == UnrealTargetPlatform.TVOS)
                    {
                        Arguments.Append(" -tvos");
                    }

                    ProcessStartInfo StartInfo = new ProcessStartInfo();
                    StartInfo.FileName  = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Binaries", "DotNET", "IOS", "IPhonePackager.exe").FullName;
                    StartInfo.Arguments = Arguments.ToString();
                    if (Utils.RunLocalProcessAndLogOutput(StartInfo) != 0)
                    {
                        throw new BuildException("IphonePackager failed.");
                    }

                    FileReference.WriteAllText(CertificateInfoFile, CertificateInfoContents);
                }

                // Upload the certificate to the remote
                Log.TraceInformation("[Remote] Uploading {0}", CertificateFile);
                UploadFile(CertificateFile);

                // Tell the remote UBT instance to use them
                RemoteArguments.Add(String.Format("-ImportProvision={0}", GetRemotePath(MobileProvisionFile)));
                RemoteArguments.Add(String.Format("-ImportCertificate={0}", GetRemotePath(CertificateFile)));
                RemoteArguments.Add(String.Format("-ImportCertificatePassword=A"));
            }

            // Upload the workspace files
            UploadWorkspace(TempDir);

            // Fixup permissions on any shell scripts
            Execute(RemoteBaseDir, String.Format("chmod +x {0}/Build/BatchFiles/Mac/*.sh", EscapeShellArgument(GetRemotePath(UnrealBuildTool.EngineDirectory))));

            // Execute the compile
            Log.TraceInformation("[Remote] Executing build");

            StringBuilder BuildCommandLine = new StringBuilder("Engine/Build/BatchFiles/Mac/Build.sh");

            foreach (string RemoteArgument in RemoteArguments)
            {
                BuildCommandLine.AppendFormat(" {0}", EscapeShellArgument(RemoteArgument));
            }

            int Result = Execute(GetRemotePath(UnrealBuildTool.RootDirectory), BuildCommandLine.ToString());

            if (Result != 0)
            {
                if (RemoteLogFile != null)
                {
                    Log.TraceInformation("[Remote] Downloading {0}", RemoteLogFile);
                    DownloadFile(RemoteLogFile);
                }
                return(false);
            }

            // Download the manifest
            Log.TraceInformation("[Remote] Downloading {0}", RemoteManifestFile);
            DownloadFile(RemoteManifestFile);

            // Convert the manifest to local form
            BuildManifest Manifest = Utils.ReadClass <BuildManifest>(RemoteManifestFile.FullName);

            for (int Idx = 0; Idx < Manifest.BuildProducts.Count; Idx++)
            {
                Manifest.BuildProducts[Idx] = GetLocalPath(Manifest.BuildProducts[Idx]).FullName;
            }

            // Download the files from the remote
            if (TargetDesc.AdditionalArguments.Any(x => x.Equals("-GenerateManifest", StringComparison.InvariantCultureIgnoreCase)))
            {
                LocalManifestFiles.Add(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Intermediate", "Build", "Manifest.xml"));
            }
            else
            {
                Log.TraceInformation("[Remote] Downloading build products");

                List <FileReference> FilesToDownload = new List <FileReference>();
                FilesToDownload.Add(RemoteLogFile);
                FilesToDownload.AddRange(Manifest.BuildProducts.Select(x => new FileReference(x)));
                DownloadFiles(FilesToDownload);
            }

            // Write out all the local manifests
            foreach (FileReference LocalManifestFile in LocalManifestFiles)
            {
                Log.TraceInformation("[Remote] Writing {0}", LocalManifestFile);
                Utils.WriteClass <BuildManifest>(Manifest, LocalManifestFile.FullName, "");
            }
            return(true);
        }