// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Tools.DotNETCommon; namespace UnrealBuildTool { /// /// Describes all of the information needed to initialize a UEBuildTarget object /// class TargetDescriptor { public FileReference ProjectFile; public string Name; public UnrealTargetPlatform Platform; public UnrealTargetConfiguration Configuration; public string Architecture; public List OnlyModules; public FileReference ForeignPlugin; public string ForceReceiptFileName; public string[] AdditionalArguments; /// /// Constructor /// /// Path to the project file /// Name of the target to build /// Platform to build for /// Configuration to build /// Architecture to build for /// Additional arguments for the target public TargetDescriptor(FileReference ProjectFile, string TargetName, UnrealTargetPlatform Platform, UnrealTargetConfiguration Configuration, string Architecture, string[] Arguments) { this.ProjectFile = ProjectFile; this.Name = TargetName; this.Platform = Platform; this.Configuration = Configuration; this.Architecture = Architecture; this.AdditionalArguments = Arguments; } /// /// Parse a list of target descriptors from the command line /// /// Command-line arguments /// Whether to use a precompiled engine distribution /// Whether to skip compiling rules assemblies /// The project file, if already set. May be updated if not. /// List of target descriptors public static List ParseCommandLine(string[] Arguments, bool bUsePrecompiled, bool bSkipRulesCompile, ref FileReference ProjectFile) { UnrealTargetPlatform Platform = UnrealTargetPlatform.Unknown; UnrealTargetConfiguration Configuration = UnrealTargetConfiguration.Unknown; List TargetNames = new List(); List TargetTypes = new List(); string Architecture = null; List OnlyModules = new List(); FileReference ForeignPlugin = null; string ForceReceiptFileName = null; List AdditionalArguments = new List(); // 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 ' syntax is no longer supported on the command line. Use '-Module=' instead."); case "-MODULEWITHSUFFIX": throw new BuildException("'-ModuleWithSuffix ' syntax is no longer supported on the command line. Use '-Module=,' instead."); case "-PLUGIN": throw new BuildException("'-Plugin ' syntax is no longer supported on the command line. Use '-Plugin=' instead."); case "-RECEIPT": throw new BuildException("'-Receipt ' syntax is no longer supported on the command line. Use '-Receipt=' instead."); default: AdditionalArguments.Add(Arguments[ArgumentIndex]); break; } } } } 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, bUsePrecompiled, bSkipRulesCompile).GetTargetNameByType(Type, Platform, Configuration, Architecture, ProjectFile, new ReadOnlyBuildVersion(BuildVersion.ReadDefault()))); } } // Create all the target descriptor List Targets = new List(); 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); } // Create the target descriptor TargetDescriptor Target = new TargetDescriptor(ProjectFile, TargetName, Platform, Configuration, Architecture, AdditionalArguments.ToArray()); 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; } /// /// Parse a single argument value, of the form -Foo=Bar /// /// The argument to parse /// The argument prefix, eg. "-Foo=" /// Receives the value of the argument /// True if the argument could be parsed, false otherwise private static bool ParseArgumentValue(string Argument, string Prefix, out string Value) { if(Argument.StartsWith(Prefix, StringComparison.InvariantCultureIgnoreCase)) { Value = Argument.Substring(Prefix.Length); return true; } else { Value = null; return false; } } } }