예제 #1
0
        public override FileItem LinkFiles(LinkEnvironment LinkEnvironment, bool bBuildImportLibraryOnly)
        {
            if (LinkEnvironment.Config.bIsBuildingDotNetAssembly)
            {
                return(FileItem.GetItemByFileReference(LinkEnvironment.Config.OutputFilePath));
            }

            bool bIsBuildingLibrary = LinkEnvironment.Config.bIsBuildingLibrary || bBuildImportLibraryOnly;
            bool bIncludeDependentLibrariesInLibrary = bIsBuildingLibrary && LinkEnvironment.Config.bIncludeDependentLibrariesInLibrary;

            // Create an action that invokes the linker.
            Action LinkAction = new Action(ActionType.Link);

            LinkAction.WorkingDirectory = UnrealBuildTool.EngineSourceDirectory.FullName;
            LinkAction.CommandPath      = GetVCToolPath(
                LinkEnvironment.Config.Target.Platform,
                LinkEnvironment.Config.Target.Configuration,
                bIsBuildingLibrary ? "lib" : "link");

            // Get link arguments.
            LinkAction.CommandArguments = bIsBuildingLibrary ?
                                          GetLibArguments(LinkEnvironment) :
                                          GetLinkArguments(LinkEnvironment);

            // Tell the action that we're building an import library here and it should conditionally be
            // ignored as a prerequisite for other actions
            LinkAction.bProducesImportLibrary = bBuildImportLibraryOnly || LinkEnvironment.Config.bIsBuildingDLL;

            // If we're only building an import library, add the '/DEF' option that tells the LIB utility
            // to simply create a .LIB file and .EXP file, and don't bother validating imports
            if (bBuildImportLibraryOnly)
            {
                LinkAction.CommandArguments += " /DEF";

                // Ensure that the import library references the correct filename for the linked binary.
                LinkAction.CommandArguments += string.Format(" /NAME:\"{0}\"", LinkEnvironment.Config.OutputFilePath.GetFileName());
            }

            if (!LinkEnvironment.Config.bIsBuildingLibrary || (LinkEnvironment.Config.bIsBuildingLibrary && bIncludeDependentLibrariesInLibrary))
            {
                // Add the library paths to the argument list.
                foreach (string LibraryPath in LinkEnvironment.Config.LibraryPaths)
                {
                    LinkAction.CommandArguments += string.Format(" /LIBPATH:\"{0}\"", LibraryPath);
                }

                // Add the excluded default libraries to the argument list.
                foreach (string ExcludedLibrary in LinkEnvironment.Config.ExcludedLibraries)
                {
                    LinkAction.CommandArguments += string.Format(" /NODEFAULTLIB:\"{0}\"", ExcludedLibrary);
                }
            }

            // For targets that are cross-referenced, we don't want to write a LIB file during the link step as that
            // file will clobber the import library we went out of our way to generate during an earlier step.  This
            // file is not needed for our builds, but there is no way to prevent MSVC from generating it when
            // linking targets that have exports.  We don't want this to clobber our LIB file and invalidate the
            // existing timstamp, so instead we simply emit it with a different name
            FileReference ImportLibraryFilePath = FileReference.Combine(LinkEnvironment.Config.IntermediateDirectory,
                                                                        LinkEnvironment.Config.OutputFilePath.GetFileNameWithoutExtension() + ".lib");

            if (LinkEnvironment.Config.bIsCrossReferenced && !bBuildImportLibraryOnly)
            {
                ImportLibraryFilePath += ".suppressed";
            }

            FileItem OutputFile;

            if (bBuildImportLibraryOnly)
            {
                OutputFile = FileItem.GetItemByFileReference(ImportLibraryFilePath);
            }
            else
            {
                OutputFile = FileItem.GetItemByFileReference(LinkEnvironment.Config.OutputFilePath);
                OutputFile.bNeedsHotReloadNumbersDLLCleanUp = LinkEnvironment.Config.bIsBuildingDLL;
            }
            LinkAction.ProducedItems.Add(OutputFile);
            LinkAction.StatusDescription = Path.GetFileName(OutputFile.AbsolutePath);

            // Add the input files to a response file, and pass the response file on the command-line.
            List <string> InputFileNames = new List <string>();

            foreach (FileItem InputFile in LinkEnvironment.InputFiles)
            {
                InputFileNames.Add(string.Format("\"{0}\"", InputFile.AbsolutePath));
                LinkAction.PrerequisiteItems.Add(InputFile);
            }

            if (!bBuildImportLibraryOnly)
            {
                // Add input libraries as prerequisites, too!
                foreach (FileItem InputLibrary in LinkEnvironment.InputLibraries)
                {
                    InputFileNames.Add(string.Format("\"{0}\"", InputLibrary.AbsolutePath));
                    LinkAction.PrerequisiteItems.Add(InputLibrary);
                }
            }

            if (!bIsBuildingLibrary || (LinkEnvironment.Config.bIsBuildingLibrary && bIncludeDependentLibrariesInLibrary))
            {
                foreach (string AdditionalLibrary in LinkEnvironment.Config.AdditionalLibraries)
                {
                    InputFileNames.Add(string.Format("\"{0}\"", AdditionalLibrary));

                    // If the library file name has a relative path attached (rather than relying on additional
                    // lib directories), then we'll add it to our prerequisites list.  This will allow UBT to detect
                    // when the binary needs to be relinked because a dependent external library has changed.
                    //if( !String.IsNullOrEmpty( Path.GetDirectoryName( AdditionalLibrary ) ) )
                    {
                        LinkAction.PrerequisiteItems.Add(FileItem.GetItemByPath(AdditionalLibrary));
                    }
                }
            }

            // Create a response file for the linker
            FileReference ResponseFileName = GetResponseFileName(LinkEnvironment, OutputFile);

            // Never create response files when we are only generating IntelliSense data
            if (!ProjectFileGenerator.bGenerateProjectFiles)
            {
                ResponseFile.Create(ResponseFileName, InputFileNames);
            }
            LinkAction.CommandArguments += string.Format(" @\"{0}\"", ResponseFileName);

            // Add the output file to the command-line.
            LinkAction.CommandArguments += string.Format(" /OUT:\"{0}\"", OutputFile.AbsolutePath);

            if (bBuildImportLibraryOnly || (LinkEnvironment.Config.bHasExports && !bIsBuildingLibrary))
            {
                // An export file is written to the output directory implicitly; add it to the produced items list.
                FileReference ExportFilePath = ImportLibraryFilePath.ChangeExtension(".exp");
                FileItem      ExportFile     = FileItem.GetItemByFileReference(ExportFilePath);
                LinkAction.ProducedItems.Add(ExportFile);
            }

            if (!bIsBuildingLibrary)
            {
                // There is anything to export
                if (LinkEnvironment.Config.bHasExports
                    // Shipping monolithic builds don't need exports
                    && (!((LinkEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Shipping) && (LinkEnvironment.bShouldCompileMonolithic != false))))
                {
                    // Write the import library to the output directory for nFringe support.
                    FileItem ImportLibraryFile = FileItem.GetItemByFileReference(ImportLibraryFilePath);
                    LinkAction.CommandArguments += string.Format(" /IMPLIB:\"{0}\"", ImportLibraryFilePath);
                    LinkAction.ProducedItems.Add(ImportLibraryFile);
                }

                if (LinkEnvironment.Config.bCreateDebugInfo)
                {
                    // Write the PDB file to the output directory.
                    {
                        FileReference PDBFilePath = FileReference.Combine(LinkEnvironment.Config.OutputDirectory, Path.GetFileNameWithoutExtension(OutputFile.AbsolutePath) + ".pdb");
                        FileItem      PDBFile     = FileItem.GetItemByFileReference(PDBFilePath);
                        LinkAction.CommandArguments += string.Format(" /PDB:\"{0}\"", PDBFilePath);
                        LinkAction.ProducedItems.Add(PDBFile);
                    }

                    // Write the MAP file to the output directory.
#if false
                    if (true)
                    {
                        FileReference MAPFilePath = FileReference.Combine(LinkEnvironment.Config.OutputDirectory, Path.GetFileNameWithoutExtension(OutputFile.AbsolutePath) + ".map");
                        FileItem      MAPFile     = FileItem.GetItemByFileReference(MAPFilePath);
                        LinkAction.CommandArguments += string.Format(" /MAP:\"{0}\"", MAPFilePath);
                        LinkAction.ProducedItems.Add(MAPFile);
                    }
#endif
                }

                // Add the additional arguments specified by the environment.
                LinkAction.CommandArguments += LinkEnvironment.Config.AdditionalArguments;
            }

            Log.TraceVerbose("     Linking: " + LinkAction.StatusDescription);
            Log.TraceVerbose("     Command: " + LinkAction.CommandArguments);

            // Only execute linking on the local PC.
            LinkAction.bCanExecuteRemotely = false;

            return(OutputFile);
        }
예제 #2
0
        public override FileItem LinkFiles(LinkEnvironment LinkEnvironment, bool bBuildImportLibraryOnly)
        {
            if (LinkEnvironment.Config.Target.Architecture == "-win32")
            {
                return(base.LinkFiles(LinkEnvironment, bBuildImportLibraryOnly));
            }

            FileItem OutputFile;

            // Make the final javascript file
            Action LinkAction = new Action(ActionType.Link);

            // ResponseFile lines.
            List <string> ReponseLines = new List <string>();

            LinkAction.bCanExecuteRemotely = false;
            LinkAction.WorkingDirectory    = Path.GetFullPath(".");
            LinkAction.CommandPath         = HTML5SDKInfo.Python();
            LinkAction.CommandArguments    = HTML5SDKInfo.EmscriptenCompiler();
            ReponseLines.Add(GetLinkArguments(LinkEnvironment));

            // Add the input files to a response file, and pass the response file on the command-line.
            foreach (FileItem InputFile in LinkEnvironment.InputFiles)
            {
                //System.Console.WriteLine("File  {0} ", InputFile.AbsolutePath);
                ReponseLines.Add(string.Format(" \"{0}\"", InputFile.AbsolutePath));
                LinkAction.PrerequisiteItems.Add(InputFile);
            }

            if (!LinkEnvironment.Config.bIsBuildingLibrary)
            {
                // Make sure ThirdParty libs are at the end.
                List <string> ThirdParty = (from Lib in LinkEnvironment.Config.AdditionalLibraries
                                            where Lib.Contains("ThirdParty")
                                            select Lib).ToList();

                LinkEnvironment.Config.AdditionalLibraries.RemoveAll(Element => Element.Contains("ThirdParty"));
                LinkEnvironment.Config.AdditionalLibraries.AddRange(ThirdParty);

                foreach (string InputFile in LinkEnvironment.Config.AdditionalLibraries)
                {
                    FileItem Item = FileItem.GetItemByPath(InputFile);

                    if (Item.AbsolutePath.Contains(".lib"))
                    {
                        continue;
                    }

                    if (Item != null)
                    {
                        if (Item.ToString().Contains(".js"))
                        {
                            ReponseLines.Add(string.Format(" --js-library \"{0}\"", Item.AbsolutePath));
                        }
                        else
                        {
                            ReponseLines.Add(string.Format(" \"{0}\"", Item.AbsolutePath));
                        }
                        LinkAction.PrerequisiteItems.Add(Item);
                    }
                }
            }
            // make the file we will create


            OutputFile = FileItem.GetItemByPath(LinkEnvironment.Config.OutputFilePath);
            LinkAction.ProducedItems.Add(OutputFile);
            ReponseLines.Add(string.Format(" -o \"{0}\"", OutputFile.AbsolutePath));

            FileItem OutputBC = FileItem.GetItemByPath(LinkEnvironment.Config.OutputFilePath.Replace(".js", ".bc").Replace(".html", ".bc"));

            LinkAction.ProducedItems.Add(OutputBC);
            ReponseLines.Add(" --emit-symbol-map " + string.Format(" --save-bc \"{0}\"", OutputBC.AbsolutePath));

            LinkAction.StatusDescription = Path.GetFileName(OutputFile.AbsolutePath);

            string ResponseFileName = GetResponseFileName(LinkEnvironment, OutputFile);


            LinkAction.CommandArguments += string.Format(" @\"{0}\"", ResponseFile.Create(ResponseFileName, ReponseLines));

            LinkAction.OutputEventHandler = new DataReceivedEventHandler(RemoteOutputReceivedEventHandler);

            return(OutputFile);
        }
예제 #3
0
        public override FileItem LinkFiles(LinkEnvironment LinkEnvironment, bool bBuildImportLibraryOnly, ActionGraph ActionGraph)
        {
            FileItem OutputFile;

            // Make the final javascript file
            Action LinkAction = ActionGraph.Add(ActionType.Link);

            LinkAction.CommandDescription = "Link";
//			LinkAction.bPrintDebugInfo = true;

            // ResponseFile lines.
            List <string> ReponseLines = new List <string>();

            LinkAction.bCanExecuteRemotely = false;
            LinkAction.WorkingDirectory    = UnrealBuildTool.EngineSourceDirectory.FullName;
            LinkAction.CommandPath         = HTML5SDKInfo.Python();
            LinkAction.CommandArguments    = HTML5SDKInfo.EmscriptenCompiler();
//			bool bIsBuildingLibrary = LinkEnvironment.bIsBuildingLibrary || bBuildImportLibraryOnly;
//			ReponseLines.Add(
//					bIsBuildingLibrary ?
//					GetLibArguments(LinkEnvironment) :
//					GetLinkArguments(LinkEnvironment)
//				);
            ReponseLines.Add(GetLinkArguments(LinkEnvironment));

            // Add the input files to a response file, and pass the response file on the command-line.
            foreach (FileItem InputFile in LinkEnvironment.InputFiles)
            {
                //System.Console.WriteLine("File  {0} ", InputFile.AbsolutePath);
                ReponseLines.Add(string.Format(" \"{0}\"", InputFile.AbsolutePath));
                LinkAction.PrerequisiteItems.Add(InputFile);
            }

            if (!LinkEnvironment.bIsBuildingLibrary)
            {
                // Make sure ThirdParty libs are at the end.
                List <string> ThirdParty = (from Lib in LinkEnvironment.AdditionalLibraries
                                            where Lib.Contains("ThirdParty")
                                            select Lib).ToList();

                LinkEnvironment.AdditionalLibraries.RemoveAll(Element => Element.Contains("ThirdParty"));
                LinkEnvironment.AdditionalLibraries.AddRange(ThirdParty);

                foreach (string InputFile in LinkEnvironment.AdditionalLibraries)
                {
                    FileItem Item = FileItem.GetItemByPath(InputFile);

                    if (Item.AbsolutePath.Contains(".lib"))
                    {
                        continue;
                    }

                    if (Item.ToString().EndsWith(".js"))
                    {
                        ReponseLines.Add(string.Format(" --js-library \"{0}\"", Item.AbsolutePath));
                    }


                    // WARNING: With --pre-js and --post-js, the order in which these directives are passed to
                    // the compiler is very critical, because that dictates the order in which they are appended.
                    //
                    // Set environment variable [ EMCC_DEBUG=1 ] to see the linker order used in packaging.
                    //     See GetSharedArguments_Global() above to set this environment variable

                    else if (Item.ToString().EndsWith(".jspre"))
                    {
                        ReponseLines.Add(string.Format(" --pre-js \"{0}\"", Item.AbsolutePath));
                    }

                    else if (Item.ToString().EndsWith(".jspost"))
                    {
                        ReponseLines.Add(string.Format(" --post-js \"{0}\"", Item.AbsolutePath));
                    }


                    else
                    {
                        ReponseLines.Add(string.Format(" \"{0}\"", Item.AbsolutePath));
                    }

                    LinkAction.PrerequisiteItems.Add(Item);
                }
            }
            // make the file we will create


            OutputFile = FileItem.GetItemByFileReference(LinkEnvironment.OutputFilePath);
            LinkAction.ProducedItems.Add(OutputFile);
            ReponseLines.Add(string.Format(" -o \"{0}\"", OutputFile.AbsolutePath));

            FileItem OutputBC = FileItem.GetItemByPath(LinkEnvironment.OutputFilePath.FullName.Replace(".js", ".bc").Replace(".html", ".bc"));

            LinkAction.ProducedItems.Add(OutputBC);
            ReponseLines.Add(string.Format(" --save-bc \"{0}\"", OutputBC.AbsolutePath));

            LinkAction.StatusDescription = Path.GetFileName(OutputFile.AbsolutePath);

            FileReference ResponseFileName = GetResponseFileName(LinkEnvironment, OutputFile);


            LinkAction.CommandArguments += string.Format(" @\"{0}\"", ResponseFile.Create(ResponseFileName, ReponseLines));

            LinkAction.OutputEventHandler = new DataReceivedEventHandler(RemoteOutputReceivedEventHandler);

            return(OutputFile);
        }
예제 #4
0
        public override FileItem[] LinkAllFiles(LinkEnvironment LinkEnvironment, bool bBuildImportLibraryOnly)
        {
            List <FileItem> Outputs = new List <FileItem>();

            for (int ArchIndex = 0; ArchIndex < Arches.Length; ArchIndex++)
            {
                string Arch = Arches[ArchIndex];
                for (int GPUArchIndex = 0; GPUArchIndex < GPUArchitectures.Length; GPUArchIndex++)
                {
                    string GPUArchitecture = GPUArchitectures[GPUArchIndex];
                    int    OutputPathIndex = ArchIndex * GPUArchitectures.Length + GPUArchIndex;

                    // Android will have an array of outputs
                    if (LinkEnvironment.Config.OutputFilePaths.Length < OutputPathIndex ||
                        !Path.GetFileNameWithoutExtension(LinkEnvironment.Config.OutputFilePaths[OutputPathIndex]).EndsWith(Arch + GPUArchitecture))
                    {
                        throw new BuildException("The OutputFilePaths array didn't match the Arches array in AndroidToolChain.LinkAllFiles");
                    }

                    // Create an action that invokes the linker.
                    Action LinkAction = new Action(ActionType.Link);
                    LinkAction.WorkingDirectory = Path.GetFullPath(".");

                    if (LinkEnvironment.Config.bIsBuildingLibrary)
                    {
                        LinkAction.CommandPath = Arch == "-armv7" ? ArPathArm : ArPathx86;
                    }
                    else
                    {
                        LinkAction.CommandPath = ClangPath;
                    }

                    string LinkerPath = LinkAction.WorkingDirectory;

                    LinkAction.WorkingDirectory = LinkEnvironment.Config.IntermediateDirectory;

                    // Get link arguments.
                    LinkAction.CommandArguments = LinkEnvironment.Config.bIsBuildingLibrary ? GetArArguments(LinkEnvironment) : GetLinkArguments(LinkEnvironment, Arch);

                    // Add the output file as a production of the link action.
                    FileItem OutputFile;
                    OutputFile = FileItem.GetItemByPath(LinkEnvironment.Config.OutputFilePaths[OutputPathIndex]);
                    Outputs.Add(OutputFile);
                    LinkAction.ProducedItems.Add(OutputFile);
                    LinkAction.StatusDescription = string.Format("{0}", Path.GetFileName(OutputFile.AbsolutePath));

                    // LinkAction.bPrintDebugInfo = true;

                    // Add the output file to the command-line.
                    if (LinkEnvironment.Config.bIsBuildingLibrary)
                    {
                        LinkAction.CommandArguments += string.Format(" \"{0}\"", OutputFile.AbsolutePath);
                    }
                    else
                    {
                        LinkAction.CommandArguments += string.Format(" -o \"{0}\"", OutputFile.AbsolutePath);
                    }

                    // Add the input files to a response file, and pass the response file on the command-line.
                    List <string> InputFileNames = new List <string>();
                    foreach (FileItem InputFile in LinkEnvironment.InputFiles)
                    {
                        // make sure it's for current Arch
                        if (Path.GetFileNameWithoutExtension(InputFile.AbsolutePath).EndsWith(Arch + GPUArchitecture))
                        {
                            string AbsolutePath = InputFile.AbsolutePath.Replace("\\", "/");

                            AbsolutePath = AbsolutePath.Replace(LinkEnvironment.Config.IntermediateDirectory.Replace("\\", "/"), "");
                            AbsolutePath = AbsolutePath.TrimStart(new char[] { '/' });

                            InputFileNames.Add(string.Format("\"{0}\"", AbsolutePath));
                            LinkAction.PrerequisiteItems.Add(InputFile);
                        }
                    }

                    string ResponseFileName = GetResponseFileName(LinkEnvironment, OutputFile);
                    LinkAction.CommandArguments += string.Format(" @\"{0}\"", ResponseFile.Create(ResponseFileName, InputFileNames));

                    // libs don't link in other libs
                    if (!LinkEnvironment.Config.bIsBuildingLibrary)
                    {
                        // Add the library paths to the argument list.
                        foreach (string LibraryPath in LinkEnvironment.Config.LibraryPaths)
                        {
                            // LinkerPaths could be relative or absolute
                            string AbsoluteLibraryPath = ActionThread.ExpandEnvironmentVariables(LibraryPath);
                            if (IsDirectoryForArch(AbsoluteLibraryPath, Arch))
                            {
                                // environment variables aren't expanded when using the $( style
                                if (Path.IsPathRooted(AbsoluteLibraryPath) == false)
                                {
                                    AbsoluteLibraryPath = Path.Combine(LinkerPath, AbsoluteLibraryPath);
                                }
                                LinkAction.CommandArguments += string.Format(" -L\"{0}\"", AbsoluteLibraryPath);
                            }
                        }

                        // add libraries in a library group
                        LinkAction.CommandArguments += string.Format(" -Wl,--start-group");
                        foreach (string AdditionalLibrary in LinkEnvironment.Config.AdditionalLibraries)
                        {
                            if (!ShouldSkipLib(AdditionalLibrary, Arch, GPUArchitecture))
                            {
                                if (String.IsNullOrEmpty(Path.GetDirectoryName(AdditionalLibrary)))
                                {
                                    LinkAction.CommandArguments += string.Format(" \"-l{0}\"", AdditionalLibrary);
                                }
                                else
                                {
                                    // full pathed libs are compiled by us, so we depend on linking them
                                    LinkAction.CommandArguments += string.Format(" \"{0}\"", Path.GetFullPath(AdditionalLibrary));
                                    LinkAction.PrerequisiteItems.Add(FileItem.GetItemByPath(AdditionalLibrary));
                                }
                            }
                        }
                        LinkAction.CommandArguments += string.Format(" -Wl,--end-group");
                    }

                    // Add the additional arguments specified by the environment.
                    LinkAction.CommandArguments += LinkEnvironment.Config.AdditionalArguments;
                    LinkAction.CommandArguments  = LinkAction.CommandArguments.Replace("\\", "/");

                    // Only execute linking on the local PC.
                    LinkAction.bCanExecuteRemotely = false;
                }
            }

            return(Outputs.ToArray());
        }
예제 #5
0
        public override CPPOutput CompileCPPFiles(UEBuildTarget Target, CPPEnvironment CompileEnvironment, List <FileItem> SourceFiles, string ModuleName)
        {
            if (Arches.Length == 0)
            {
                throw new BuildException("At least one architecture (armv7, x86, etc) needs to be selected in the project settings to build");
            }

            if (!bHasPrintedApiLevel)
            {
                Console.WriteLine("Compiling Native code with NDK API '{0}'", GetNdkApiLevel());
                bHasPrintedApiLevel = true;
            }

            string BaseArguments = "";

            if (CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create)
            {
                BaseArguments += " -Werror";
            }

            // Directly added NDK files for NDK extensions
            ConditionallyAddNDKSourceFiles(SourceFiles, ModuleName);

            // Add preprocessor definitions to the argument list.
            foreach (string Definition in CompileEnvironment.Config.Definitions)
            {
                BaseArguments += string.Format(" -D \"{0}\"", Definition);
            }

            var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform);



            string BasePCHName  = "";
            var    PCHExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.Android].GetBinaryExtension(UEBuildBinaryType.PrecompiledHeader);

            if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
            {
                BasePCHName = RemoveArchName(CompileEnvironment.PrecompiledHeaderFile.AbsolutePath).Replace(PCHExtension, "");
            }

            // Create a compile action for each source file.
            CPPOutput Result = new CPPOutput();

            foreach (string Arch in Arches)
            {
                foreach (string GPUArchitecture in GPUArchitectures)
                {
                    // which toolchain to use
                    string Arguments = GetCLArguments_Global(CompileEnvironment, Arch) + BaseArguments;

                    if (GPUArchitecture == "-gl4")
                    {
                        Arguments += " -DPLATFORM_ANDROIDGL4=1";
                    }
                    else if (GPUArchitecture == "-es31")
                    {
                        Arguments += " -DPLATFORM_ANDROIDES31=1";
                    }

                    // which PCH file to include
                    string PCHArguments = "";
                    if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
                    {
                        // Add the precompiled header file's path to the include path so Clang can find it.
                        // This needs to be before the other include paths to ensure Clang uses it instead of the source header file.
                        PCHArguments += string.Format(" -include \"{0}\"", InlineArchName(BasePCHName, Arch, GPUArchitecture));
                    }

                    // Add include paths to the argument list (filtered by architecture)
                    foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths)
                    {
                        if (IsDirectoryForArch(IncludePath, Arch))
                        {
                            Arguments += string.Format(" -I\"{0}\"", IncludePath);
                        }
                    }
                    foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths)
                    {
                        if (IsDirectoryForArch(IncludePath, Arch))
                        {
                            Arguments += string.Format(" -I\"{0}\"", IncludePath);
                        }
                    }

                    foreach (FileItem SourceFile in SourceFiles)
                    {
                        Action CompileAction = new Action(ActionType.Compile);
                        string FileArguments = "";
                        bool   bIsPlainCFile = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant() == ".C";

                        // should we disable optimizations on this file?
                        // @todo android - We wouldn't need this if we could disable optimizations per function (via pragma)
                        bool bDisableOptimizations = false;                        // SourceFile.AbsolutePath.ToUpperInvariant().IndexOf("\\SLATE\\") != -1;
                        if (bDisableOptimizations && CompileEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Debug)
                        {
                            Log.TraceWarning("Disabling optimizations on {0}", SourceFile.AbsolutePath);
                        }

                        bDisableOptimizations = bDisableOptimizations || CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug;

                        // Add C or C++ specific compiler arguments.
                        if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
                        {
                            FileArguments += GetCompileArguments_PCH(bDisableOptimizations);
                        }
                        else if (bIsPlainCFile)
                        {
                            FileArguments += GetCompileArguments_C(bDisableOptimizations);
                        }
                        else
                        {
                            FileArguments += GetCompileArguments_CPP(bDisableOptimizations);

                            // only use PCH for .cpp files
                            FileArguments += PCHArguments;
                        }

                        // Add the C++ source file and its included files to the prerequisite item list.
                        AddPrerequisiteSourceFile(Target, BuildPlatform, CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems);

                        if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
                        {
                            // Add the precompiled header file to the produced item list.
                            FileItem PrecompiledHeaderFile = FileItem.GetItemByPath(
                                Path.Combine(
                                    CompileEnvironment.Config.OutputDirectory,
                                    Path.GetFileName(InlineArchName(SourceFile.AbsolutePath, Arch, GPUArchitecture) + PCHExtension)
                                    )
                                );

                            CompileAction.ProducedItems.Add(PrecompiledHeaderFile);
                            Result.PrecompiledHeaderFile = PrecompiledHeaderFile;

                            // Add the parameters needed to compile the precompiled header file to the command-line.
                            FileArguments += string.Format(" -o \"{0}\"", PrecompiledHeaderFile.AbsolutePath, false);
                        }
                        else
                        {
                            if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
                            {
                                CompileAction.bIsUsingPCH = true;
                                FileItem ArchPrecompiledHeaderFile = FileItem.GetItemByPath(InlineArchName(BasePCHName, Arch, GPUArchitecture) + PCHExtension);
                                CompileAction.PrerequisiteItems.Add(ArchPrecompiledHeaderFile);
                            }

                            var ObjectFileExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.Android].GetBinaryExtension(UEBuildBinaryType.Object);

                            // Add the object file to the produced item list.
                            FileItem ObjectFile = FileItem.GetItemByPath(
                                InlineArchName(Path.Combine(
                                                   CompileEnvironment.Config.OutputDirectory,
                                                   Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension), Arch, GPUArchitecture
                                               )
                                );
                            CompileAction.ProducedItems.Add(ObjectFile);
                            Result.ObjectFiles.Add(ObjectFile);

                            FileArguments += string.Format(" -o \"{0}\"", ObjectFile.AbsolutePath, false);
                        }

                        // Add the source file path to the command-line.
                        FileArguments += string.Format(" \"{0}\"", SourceFile.AbsolutePath);

                        // Build a full argument list
                        string AllArguments = Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments;
                        AllArguments = ActionThread.ExpandEnvironmentVariables(AllArguments);
                        AllArguments = AllArguments.Replace("\\", "/");

                        // Create the response file
                        string ResponseFileName = CompileAction.ProducedItems[0].AbsolutePath + ".response";
                        string ResponseArgument = string.Format("@\"{0}\"", ResponseFile.Create(ResponseFileName, new List <string> {
                            AllArguments
                        }));

                        CompileAction.WorkingDirectory  = Path.GetFullPath(".");
                        CompileAction.CommandPath       = ClangPath;
                        CompileAction.CommandArguments  = ResponseArgument;
                        CompileAction.StatusDescription = string.Format("{0} [{1}-{2}]", Path.GetFileName(SourceFile.AbsolutePath), Arch.Replace("-", ""), GPUArchitecture.Replace("-", ""));

                        CompileAction.OutputEventHandler = new DataReceivedEventHandler(CompileOutputReceivedDataEventHandler);

                        // VC++ always outputs the source file name being compiled, so we don't need to emit this ourselves
                        CompileAction.bShouldOutputStatusDescription = true;

                        // Don't farm out creation of pre-compiled headers as it is the critical path task.
                        CompileAction.bCanExecuteRemotely =
                            CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create ||
                            BuildConfiguration.bAllowRemotelyCompiledPCHs;
                    }
                }
            }

            return(Result);
        }
예제 #6
0
        public override FileItem LinkFiles(LinkEnvironment LinkEnvironment, bool bBuildImportLibraryOnly)
        {
            Debug.Assert(!bBuildImportLibraryOnly);

            List <string> RPaths = new List <string>();

            if (LinkEnvironment.Config.bIsBuildingLibrary || bBuildImportLibraryOnly)
            {
                return(CreateArchiveAndIndex(LinkEnvironment));
            }

            // Create an action that invokes the linker.
            Action LinkAction = new Action(ActionType.Link);

            LinkAction.WorkingDirectory = Path.GetFullPath(".");
            if (String.IsNullOrEmpty(ClangPath))
            {
                LinkAction.CommandPath = GCCPath;
            }
            else
            {
                LinkAction.CommandPath = ClangPath;
            }

            // Get link arguments.
            LinkAction.CommandArguments = GetLinkArguments(LinkEnvironment);

            // Tell the action that we're building an import library here and it should conditionally be
            // ignored as a prerequisite for other actions
            LinkAction.bProducesImportLibrary = LinkEnvironment.Config.bIsBuildingDLL;

            // Add the output file as a production of the link action.
            FileItem OutputFile = FileItem.GetItemByPath(LinkEnvironment.Config.OutputFilePath);

            LinkAction.ProducedItems.Add(OutputFile);
            LinkAction.CommandDescription = "Link";
            LinkAction.StatusDescription  = Path.GetFileName(OutputFile.AbsolutePath);

            // Add the output file to the command-line.
            LinkAction.CommandArguments += string.Format(" -o \"{0}\"", OutputFile.AbsolutePath);

            // Add the input files to a response file, and pass the response file on the command-line.
            List <string> InputFileNames = new List <string>();

            foreach (FileItem InputFile in LinkEnvironment.InputFiles)
            {
                InputFileNames.Add(string.Format("\"{0}\"", InputFile.AbsolutePath.Replace("\\", "/")));
                LinkAction.PrerequisiteItems.Add(InputFile);
            }

            string ResponseFileName = GetResponseFileName(LinkEnvironment, OutputFile);

            LinkAction.CommandArguments += string.Format(" -Wl,@\"{0}\"", ResponseFile.Create(ResponseFileName, InputFileNames));

            if (LinkEnvironment.Config.bIsBuildingDLL)
            {
                LinkAction.CommandArguments += string.Format(" -Wl,-soname={0}", OutputFile);
            }

            // Start with the configured LibraryPaths and also add paths to any libraries that
            // we depend on (libraries that we've build ourselves).
            List <string> AllLibraryPaths = LinkEnvironment.Config.LibraryPaths;

            foreach (string AdditionalLibrary in LinkEnvironment.Config.AdditionalLibraries)
            {
                string PathToLib = Path.GetDirectoryName(AdditionalLibrary);
                if (!String.IsNullOrEmpty(PathToLib))
                {
                    // make path absolute, because FixDependencies script may be executed in a different directory
                    string AbsolutePathToLib = Path.GetFullPath(PathToLib);
                    if (!AllLibraryPaths.Contains(AbsolutePathToLib))
                    {
                        AllLibraryPaths.Add(AbsolutePathToLib);
                    }
                }

                if ((AdditionalLibrary.Contains("Plugins") || AdditionalLibrary.Contains("Binaries/ThirdParty") || AdditionalLibrary.Contains("Binaries\\ThirdParty")) && Path.GetDirectoryName(AdditionalLibrary) != Path.GetDirectoryName(OutputFile.AbsolutePath))
                {
                    string RelativePath = Utils.MakePathRelativeTo(Path.GetDirectoryName(AdditionalLibrary), Path.GetDirectoryName(OutputFile.AbsolutePath));
                    if (!RPaths.Contains(RelativePath))
                    {
                        RPaths.Add(RelativePath);
                        LinkAction.CommandArguments += string.Format(" -Wl,-rpath=\"${{ORIGIN}}/{0}\"", RelativePath);
                    }
                }
            }

            LinkAction.CommandArguments += string.Format(" -Wl,-rpath-link=\"{0}\"", Path.GetDirectoryName(OutputFile.AbsolutePath));

            // Add the library paths to the argument list.
            foreach (string LibraryPath in AllLibraryPaths)
            {
                // use absolute paths because of FixDependencies script again
                LinkAction.CommandArguments += string.Format(" -L\"{0}\"", Path.GetFullPath(LibraryPath));
            }

            // add libraries in a library group
            LinkAction.CommandArguments += string.Format(" -Wl,--start-group");

            List <string> EngineAndGameLibraries = new List <string>();

            foreach (string AdditionalLibrary in LinkEnvironment.Config.AdditionalLibraries)
            {
                if (String.IsNullOrEmpty(Path.GetDirectoryName(AdditionalLibrary)))
                {
                    // library was passed just like "jemalloc", turn it into -ljemalloc
                    LinkAction.CommandArguments += string.Format(" -l{0}", AdditionalLibrary);
                }
                else if (Path.GetExtension(AdditionalLibrary) == ".a")
                {
                    // static library passed in, pass it along but make path absolute, because FixDependencies script may be executed in a different directory
                    string AbsoluteAdditionalLibrary = Path.GetFullPath(AdditionalLibrary);
                    LinkAction.CommandArguments += (" " + AbsoluteAdditionalLibrary);
                    LinkAction.PrerequisiteItems.Add(FileItem.GetItemByPath(AdditionalLibrary));
                }
                else
                {
                    // Skip over full-pathed library dependencies when building DLLs to avoid circular
                    // dependencies.
                    FileItem LibraryDependency = FileItem.GetItemByPath(AdditionalLibrary);

                    var LibName = Path.GetFileNameWithoutExtension(AdditionalLibrary);
                    if (LibName.StartsWith("lib"))
                    {
                        // Remove lib prefix
                        LibName = LibName.Remove(0, 3);
                    }
                    string LibLinkFlag = string.Format(" -l{0}", LibName);

                    if (LinkEnvironment.Config.bIsBuildingDLL && LinkEnvironment.Config.bIsCrossReferenced)
                    {
                        // We are building a cross referenced DLL so we can't actually include
                        // dependencies at this point. Instead we add it to the list of
                        // libraries to be used in the FixDependencies step.
                        EngineAndGameLibraries.Add(LibLinkFlag);
                        if (!LinkAction.CommandArguments.Contains("--allow-shlib-undefined"))
                        {
                            LinkAction.CommandArguments += string.Format(" -Wl,--allow-shlib-undefined");
                        }
                    }
                    else
                    {
                        LinkAction.PrerequisiteItems.Add(LibraryDependency);
                        LinkAction.CommandArguments += LibLinkFlag;
                    }
                }
            }
            LinkAction.CommandArguments += " -lrt"; // needed for clock_gettime()
            LinkAction.CommandArguments += " -lm";  // math
            LinkAction.CommandArguments += string.Format(" -Wl,--end-group");

            // Add the additional arguments specified by the environment.
            LinkAction.CommandArguments += LinkEnvironment.Config.AdditionalArguments;
            LinkAction.CommandArguments  = LinkAction.CommandArguments.Replace("\\\\", "/");
            LinkAction.CommandArguments  = LinkAction.CommandArguments.Replace("\\", "/");

            // prepare a linker script
            string LinkerScriptPath = Path.Combine(LinkEnvironment.Config.LocalShadowDirectory, "remove-sym.ldscript");

            if (!Directory.Exists(LinkEnvironment.Config.LocalShadowDirectory))
            {
                Directory.CreateDirectory(LinkEnvironment.Config.LocalShadowDirectory);
            }
            if (File.Exists(LinkerScriptPath))
            {
                File.Delete(LinkerScriptPath);
            }

            using (StreamWriter Writer = File.CreateText(LinkerScriptPath))
            {
                Writer.WriteLine("UE4 {");
                Writer.WriteLine("  global: *;");
                Writer.WriteLine("  local: _Znwm;");
                Writer.WriteLine("         _Znam;");
                Writer.WriteLine("         _ZdaPv;");
                Writer.WriteLine("         _ZdlPv;");
                Writer.WriteLine("};");
            };

            LinkAction.CommandArguments += string.Format(" -Wl,--version-script=\"{0}\"", LinkerScriptPath);

            // Only execute linking on the local PC.
            LinkAction.bCanExecuteRemotely = false;

            // Prepare a script that will run later, once all shared libraries and the executable
            // are created. This script will be called by action created in FixDependencies()
            if (LinkEnvironment.Config.bIsCrossReferenced && LinkEnvironment.Config.bIsBuildingDLL)
            {
                bool   bUseCmdExe = BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64 || BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win32;
                string ScriptName = bUseCmdExe ? "FixDependencies.bat" : "FixDependencies.sh";

                string FixDepsScriptPath = Path.Combine(LinkEnvironment.Config.LocalShadowDirectory, ScriptName);
                if (!bHasWipedFixDepsScript)
                {
                    bHasWipedFixDepsScript = true;
                    Log.TraceVerbose("Creating script: {0}", FixDepsScriptPath);
                    StreamWriter Writer = File.CreateText(FixDepsScriptPath);

                    if (bUseCmdExe)
                    {
                        Writer.Write("@echo off\n");
                        Writer.Write("rem Automatically generated by UnrealBuildTool\n");
                        Writer.Write("rem *DO NOT EDIT*\n\n");
                    }
                    else
                    {
                        Writer.Write("#!/bin/sh\n");
                        Writer.Write("# Automatically generated by UnrealBuildTool\n");
                        Writer.Write("# *DO NOT EDIT*\n\n");
                        Writer.Write("set -o errexit\n");
                    }
                    Writer.Close();
                }

                StreamWriter FixDepsScript = File.AppendText(FixDepsScriptPath);

                string EngineAndGameLibrariesString = "";
                foreach (string Library in EngineAndGameLibraries)
                {
                    EngineAndGameLibrariesString += Library;
                }

                FixDepsScript.Write(string.Format("echo Fixing {0}\n", Path.GetFileName(OutputFile.AbsolutePath)));
                if (!bUseCmdExe)
                {
                    FixDepsScript.Write(string.Format("TIMESTAMP=`stat --format %y \"{0}\"`\n", OutputFile.AbsolutePath));
                }
                string FixDepsLine = LinkAction.CommandPath + " " + LinkAction.CommandArguments;
                string Replace     = "-Wl,--allow-shlib-undefined";

                FixDepsLine = FixDepsLine.Replace(Replace, EngineAndGameLibrariesString);
                string OutputFileForwardSlashes = OutputFile.AbsolutePath.Replace("\\", "/");
                FixDepsLine = FixDepsLine.Replace(OutputFileForwardSlashes, OutputFileForwardSlashes + ".fixed");
                FixDepsLine = FixDepsLine.Replace("$", "\\$");
                FixDepsScript.Write(FixDepsLine + "\n");
                if (bUseCmdExe)
                {
                    FixDepsScript.Write(string.Format("move /Y \"{0}.fixed\" \"{0}\"\n", OutputFile.AbsolutePath));
                }
                else
                {
                    FixDepsScript.Write(string.Format("mv \"{0}.fixed\" \"{0}\"\n", OutputFile.AbsolutePath));
                    FixDepsScript.Write(string.Format("touch -d \"$TIMESTAMP\" \"{0}\"\n\n", OutputFile.AbsolutePath));
                }
                FixDepsScript.Close();
            }

            //LinkAction.CommandArguments += " -v";

            return(OutputFile);
        }
예제 #7
0
        public override FileItem LinkFiles(LinkEnvironment LinkEnvironment, bool bBuildImportLibraryOnly)
        {
            var EnvVars = VCEnvironment.SetEnvironment(LinkEnvironment.Config.Target.Platform);

            if (LinkEnvironment.Config.bIsBuildingDotNetAssembly)
            {
                return(FileItem.GetItemByPath(LinkEnvironment.Config.OutputFilePath));
            }

            bool bIsBuildingLibrary = LinkEnvironment.Config.bIsBuildingLibrary || bBuildImportLibraryOnly;
            bool bIncludeDependentLibrariesInLibrary = bIsBuildingLibrary && LinkEnvironment.Config.bIncludeDependentLibrariesInLibrary;

            // Get link arguments.
            StringBuilder Arguments = new StringBuilder();

            if (bIsBuildingLibrary)
            {
                AppendLibArguments(LinkEnvironment, Arguments);
            }
            else
            {
                AppendLinkArguments(LinkEnvironment, Arguments);
            }



            // If we're only building an import library, add the '/DEF' option that tells the LIB utility
            // to simply create a .LIB file and .EXP file, and don't bother validating imports
            if (bBuildImportLibraryOnly)
            {
                Arguments.Append(" /DEF");

                // Ensure that the import library references the correct filename for the linked binary.
                Arguments.AppendFormat(" /NAME:\"{0}\"", Path.GetFileName(LinkEnvironment.Config.OutputFilePath));
            }


            // Add delay loaded DLLs.
            if (!bIsBuildingLibrary)
            {
                // Delay-load these DLLs.
                foreach (string DelayLoadDLL in LinkEnvironment.Config.DelayLoadDLLs)
                {
                    Arguments.AppendFormat(" /DELAYLOAD:\"{0}\"", DelayLoadDLL);
                }
            }

            // @todo UE4 DLL: Why do I need LIBPATHs to build only export libraries with /DEF? (tbbmalloc.lib)
            if (!LinkEnvironment.Config.bIsBuildingLibrary || (LinkEnvironment.Config.bIsBuildingLibrary && bIncludeDependentLibrariesInLibrary))
            {
                // Add the library paths to the argument list.
                foreach (string LibraryPath in LinkEnvironment.Config.LibraryPaths)
                {
                    Arguments.AppendFormat(" /LIBPATH:\"{0}\"", LibraryPath);
                }

                // Add the excluded default libraries to the argument list.
                foreach (string ExcludedLibrary in LinkEnvironment.Config.ExcludedLibraries)
                {
                    Arguments.AppendFormat(" /NODEFAULTLIB:\"{0}\"", ExcludedLibrary);
                }
            }

            // For targets that are cross-referenced, we don't want to write a LIB file during the link step as that
            // file will clobber the import library we went out of our way to generate during an earlier step.  This
            // file is not needed for our builds, but there is no way to prevent MSVC from generating it when
            // linking targets that have exports.  We don't want this to clobber our LIB file and invalidate the
            // existing timstamp, so instead we simply emit it with a different name
            string ImportLibraryFilePath = Path.Combine(LinkEnvironment.Config.IntermediateDirectory,
                                                        Path.GetFileNameWithoutExtension(LinkEnvironment.Config.OutputFilePath) + ".lib");

            if (LinkEnvironment.Config.bIsCrossReferenced && !bBuildImportLibraryOnly)
            {
                ImportLibraryFilePath += ".suppressed";
            }

            FileItem OutputFile;

            if (bBuildImportLibraryOnly)
            {
                OutputFile = FileItem.GetItemByPath(ImportLibraryFilePath);
            }
            else
            {
                OutputFile = FileItem.GetItemByPath(LinkEnvironment.Config.OutputFilePath);
                OutputFile.bNeedsHotReloadNumbersDLLCleanUp = LinkEnvironment.Config.bIsBuildingDLL;
            }

            List <FileItem> ProducedItems = new List <FileItem>();

            ProducedItems.Add(OutputFile);

            List <FileItem> PrerequisiteItems = new List <FileItem>();

            // Add the input files to a response file, and pass the response file on the command-line.
            List <string> InputFileNames = new List <string>();

            foreach (FileItem InputFile in LinkEnvironment.InputFiles)
            {
                InputFileNames.Add(string.Format("\"{0}\"", InputFile.AbsolutePath));
                PrerequisiteItems.Add(InputFile);
            }

            if (!bBuildImportLibraryOnly)
            {
                // Add input libraries as prerequisites, too!
                foreach (FileItem InputLibrary in LinkEnvironment.InputLibraries)
                {
                    InputFileNames.Add(string.Format("\"{0}\"", InputLibrary.AbsolutePath));
                    PrerequisiteItems.Add(InputLibrary);
                }
            }

            if (!bIsBuildingLibrary || (LinkEnvironment.Config.bIsBuildingLibrary && bIncludeDependentLibrariesInLibrary))
            {
                foreach (string AdditionalLibrary in LinkEnvironment.Config.AdditionalLibraries)
                {
                    InputFileNames.Add(string.Format("\"{0}\"", AdditionalLibrary));

                    // If the library file name has a relative path attached (rather than relying on additional
                    // lib directories), then we'll add it to our prerequisites list.  This will allow UBT to detect
                    // when the binary needs to be relinked because a dependent external library has changed.
                    //if( !String.IsNullOrEmpty( Path.GetDirectoryName( AdditionalLibrary ) ) )
                    {
                        PrerequisiteItems.Add(FileItem.GetItemByPath(AdditionalLibrary));
                    }
                }
            }

            // Create a response file for the linker
            string ResponseFileName = GetResponseFileName(LinkEnvironment, OutputFile);

            // Never create response files when we are only generating IntelliSense data
            if (!ProjectFileGenerator.bGenerateProjectFiles)
            {
                ResponseFile.Create(ResponseFileName, InputFileNames);
            }
            Arguments.AppendFormat(" @\"{0}\"", ResponseFileName);

            // Add the output file to the command-line.
            Arguments.AppendFormat(" /OUT:\"{0}\"", OutputFile.AbsolutePath);

            if (bBuildImportLibraryOnly || (LinkEnvironment.Config.bHasExports && !bIsBuildingLibrary))
            {
                // An export file is written to the output directory implicitly; add it to the produced items list.
                string   ExportFilePath = Path.ChangeExtension(ImportLibraryFilePath, ".exp");
                FileItem ExportFile     = FileItem.GetItemByPath(ExportFilePath);
                ProducedItems.Add(ExportFile);
            }

            if (!bIsBuildingLibrary)
            {
                // Xbox 360 LTCG does not seem to produce those.
                if (LinkEnvironment.Config.bHasExports &&
                    (LinkEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Shipping))
                {
                    // Write the import library to the output directory for nFringe support.
                    FileItem ImportLibraryFile = FileItem.GetItemByPath(ImportLibraryFilePath);
                    Arguments.AppendFormat(" /IMPLIB:\"{0}\"", ImportLibraryFilePath);
                    ProducedItems.Add(ImportLibraryFile);
                }

                if (LinkEnvironment.Config.bCreateDebugInfo)
                {
                    // Write the PDB file to the output directory.
                    string   PDBFilePath = Path.Combine(LinkEnvironment.Config.OutputDirectory, Path.GetFileNameWithoutExtension(OutputFile.AbsolutePath) + ".pdb");
                    FileItem PDBFile     = FileItem.GetItemByPath(PDBFilePath);
                    Arguments.AppendFormat(" /PDB:\"{0}\"", PDBFilePath);
                    ProducedItems.Add(PDBFile);

                    // Write the MAP file to the output directory.
#if false
                    if (true)
                    {
                        string   MAPFilePath = Path.Combine(LinkEnvironment.Config.OutputDirectory, Path.GetFileNameWithoutExtension(OutputFile.AbsolutePath) + ".map");
                        FileItem MAPFile     = FileItem.GetItemByPath(MAPFilePath);
                        LinkAction.CommandArguments += string.Format(" /MAP:\"{0}\"", MAPFilePath);
                        LinkAction.ProducedItems.Add(MAPFile);
                    }
#endif
                }

                // Add the additional arguments specified by the environment.
                Arguments.Append(LinkEnvironment.Config.AdditionalArguments);
            }

            // Create an action that invokes the linker.
            Action LinkAction = new Action(ActionType.Link);
            LinkAction.CommandDescription = "Link";
            LinkAction.WorkingDirectory   = Path.GetFullPath(".");
            LinkAction.CommandPath        = bIsBuildingLibrary ? EnvVars.LibraryLinkerPath : EnvVars.LinkerPath;
            LinkAction.CommandArguments   = Arguments.ToString();
            LinkAction.ProducedItems.AddRange(ProducedItems);
            LinkAction.PrerequisiteItems.AddRange(PrerequisiteItems);
            LinkAction.StatusDescription = Path.GetFileName(OutputFile.AbsolutePath);

            // Tell the action that we're building an import library here and it should conditionally be
            // ignored as a prerequisite for other actions
            LinkAction.bProducesImportLibrary = bBuildImportLibraryOnly || LinkEnvironment.Config.bIsBuildingDLL;

            // Only execute linking on the local PC.
            LinkAction.bCanExecuteRemotely = false;

            Log.TraceVerbose("     Linking: " + LinkAction.StatusDescription);
            Log.TraceVerbose("     Command: " + LinkAction.CommandArguments);

            return(OutputFile);
        }