예제 #1
0
        /// <summary>
        /// Loads a plugin descriptor file and fills out a new PluginInfo structure.  Throws an exception on failure.
        /// </summary>
        /// <param name="PluginFile">The path to the plugin file to load</param>
        /// <param name="LoadedFrom">Where the plugin was loaded from</param>
        /// <returns>New PluginInfo for the loaded descriptor.</returns>
        private static PluginInfo LoadPluginDescriptor(FileInfo PluginFileInfo, PluginInfo.LoadedFromType LoadedFrom)
        {
            // Load the file up (JSon format)
            Dictionary<string, object> PluginDescriptorDict;
            {
                string FileContent;
                using (var StreamReader = new StreamReader(PluginFileInfo.FullName))
                {
                    FileContent = StreamReader.ReadToEnd();
                }

                // Parse the Json into a dictionary
                var CaseSensitiveJSonDict = fastJSON.JSON.Instance.ToObject<Dictionary<string, object>>(FileContent);

                // Convert to a case-insensitive dictionary, so that we can be more tolerant of hand-typed files
                PluginDescriptorDict = new Dictionary<string, object>(CaseSensitiveJSonDict, StringComparer.InvariantCultureIgnoreCase);
            }

            // File version check
            long PluginVersionNumber;
            {
                // Try to get the version of the plugin
                object PluginVersionObject;
                if (!PluginDescriptorDict.TryGetValue("FileVersion", out PluginVersionObject))
                {
                    if (!PluginDescriptorDict.TryGetValue("PluginFileVersion", out PluginVersionObject))
                    {
                        throw new BuildException("Plugin descriptor file '{0}' does not contain a valid FileVersion entry", PluginFileInfo.FullName);
                    }
                }

                if (!(PluginVersionObject is long))
                {
                    throw new BuildException("Unable to parse the version number of the plugin descriptor file '{0}'", PluginFileInfo.FullName);
                }

                PluginVersionNumber = (long)PluginVersionObject;
                if (PluginVersionNumber > LatestPluginDescriptorFileVersion)
                {
                    throw new BuildException("Plugin descriptor file '{0}' appears to be in a newer version ({1}) of the file format that we can load (max version: {2}).", PluginFileInfo.FullName, PluginVersionNumber, LatestPluginDescriptorFileVersion);
                }

                // @todo plugin: Should we also test the engine version here?  (we would need to load it from build.properties)
            }

            // NOTE: At this point, we can use PluginVersionNumber to handle backwards compatibility when loading the rest of the file!

            var PluginInfo = new PluginInfo();
            PluginInfo.LoadedFrom = LoadedFrom;
            PluginInfo.Directory = PluginFileInfo.Directory.FullName;
            PluginInfo.Name = Path.GetFileName(PluginInfo.Directory);

            // Determine whether the plugin should be enabled by default
            object EnabledByDefaultObject;
            if (PluginDescriptorDict.TryGetValue("EnabledByDefault", out EnabledByDefaultObject) && (EnabledByDefaultObject is bool))
            {
                PluginInfo.bEnabledByDefault = (bool)EnabledByDefaultObject;
            }

            // This plugin might have some modules that we need to know about.  Let's take a look.
            {
                object ModulesObject;
                if (PluginDescriptorDict.TryGetValue("Modules", out ModulesObject))
                {
                    if (!(ModulesObject is Array))
                    {
                        throw new BuildException("Found a 'Modules' entry in plugin descriptor file '{0}', but it doesn't appear to be in the array format that we were expecting.", PluginFileInfo.FullName);
                    }

                    var ModulesArray = (Array)ModulesObject;
                    foreach (var ModuleObject in ModulesArray)
                    {
                        var ModuleDict = new Dictionary<string, object>((Dictionary<string, object>)ModuleObject, StringComparer.InvariantCultureIgnoreCase);

                        var PluginModuleInfo = new PluginInfo.PluginModuleInfo();

                        // Module name
                        {
                            // All modules require a name to be set
                            object ModuleNameObject;
                            if (!ModuleDict.TryGetValue("Name", out ModuleNameObject))
                            {
                                throw new BuildException("Found a 'Module' entry with a missing 'Name' field in plugin descriptor file '{0}'", PluginFileInfo.FullName);
                            }
                            string ModuleName = (string)ModuleNameObject;

                            // @todo plugin: Locate this module right now and validate it?  Repair case?
                            PluginModuleInfo.Name = ModuleName;
                        }

                        // Module type
                        {
                            // Check to see if the user specified the module's type
                            object ModuleTypeObject;
                            if (!ModuleDict.TryGetValue("Type", out ModuleTypeObject))
                            {
                                throw new BuildException("Found a Module entry '{0}' with a missing 'Type' field in plugin descriptor file '{1}'", PluginModuleInfo.Name, PluginFileInfo.FullName);
                            }
                            string ModuleTypeString = (string)ModuleTypeObject;

                            // Check to see if this is a valid type
                            bool FoundValidType = false;
                            foreach (PluginInfo.PluginModuleType PossibleType in Enum.GetValues(typeof(PluginInfo.PluginModuleType)))
                            {
                                if (ModuleTypeString.Equals(PossibleType.ToString(), StringComparison.InvariantCultureIgnoreCase))
                                {
                                    FoundValidType = true;
                                    PluginModuleInfo.Type = PossibleType;
                                    break;
                                }
                            }
                            if (!FoundValidType)
                            {
                                throw new BuildException("Module entry '{0}' specified an unrecognized module Type '{1}' in plugin descriptor file '{0}'", PluginModuleInfo.Name, ModuleTypeString, PluginFileInfo.FullName);
                            }
                        }

                        // Supported platforms
                        PluginModuleInfo.Platforms = new List<STTargetPlatform>();

                        // look for white and blacklists
                        object WhitelistObject, BlacklistObject;
                        ModuleDict.TryGetValue("WhitelistPlatforms", out WhitelistObject);
                        ModuleDict.TryGetValue("BlacklistPlatforms", out BlacklistObject);

                        if (WhitelistObject != null && BlacklistObject != null)
                        {
                            throw new BuildException("Found a module '{0}' with both blacklist and whitelist platform lists in plugin file '{1}'", PluginModuleInfo.Name, PluginFileInfo.FullName);
                        }

                        // now process the whitelist
                        if (WhitelistObject != null)
                        {
                            if (!(WhitelistObject is Array))
                            {
                                throw new BuildException("Found a 'WhitelistPlatforms' entry in plugin descriptor file '{0}', but it doesn't appear to be in the array format that we were expecting.", PluginFileInfo.FullName);
                            }

                            // put the whitelist array directly into the plugin's modulelist
                            ConvertPlatformArrayToList((Array)WhitelistObject, ref PluginModuleInfo.Platforms, PluginFileInfo.FullName);
                        }
                        // handle the blacklist (or lack of blacklist and whitelist which means all platforms)
                        else
                        {
                            // start with all platforms supported
                            foreach (STTargetPlatform Platform in Enum.GetValues(typeof(STTargetPlatform)))
                            {
                                PluginModuleInfo.Platforms.Add(Platform);
                            }

                            // if we want to disallow some platforms, then pull them out now
                            if (BlacklistObject != null)
                            {
                                if (!(BlacklistObject is Array))
                                {
                                    throw new BuildException("Found a 'BlacklistPlatforms' entry in plugin descriptor file '{0}', but it doesn't appear to be in the array format that we were expecting.", PluginFileInfo.FullName);
                                }

                                // put the whitelist array directly into the plugin's modulelist
                                List<STTargetPlatform> Blacklist = new List<STTargetPlatform>();
                                ConvertPlatformArrayToList((Array)BlacklistObject, ref Blacklist, PluginFileInfo.FullName);

                                // now remove them from the module platform list
                                foreach (STTargetPlatform Platform in Blacklist)
                                {
                                    PluginModuleInfo.Platforms.Remove(Platform);
                                }
                            }
                        }

                        object ModuleShouldBuild;
                        if (ModuleDict.TryGetValue("bShouldBuild", out ModuleShouldBuild))
                        {
                            PluginInfo.bShouldBuild = (Int64)ModuleShouldBuild == 1 ? true : false;
                        }
                        else
                        {
                            PluginInfo.bShouldBuild = true;
                        }

                        if (PluginInfo.bShouldBuild)
                        {
                            // add to list of modules
                            PluginInfo.Modules.Add(PluginModuleInfo);
                        }
                    }
                }
                else
                {
                    // Plugin contains no modules array.  That's fine.
                }
            }

            return PluginInfo;
        }
예제 #2
0
 /// <summary>
 /// Finds all plugins in the specified base directory
 /// </summary>
 /// <param name="PluginsDirectory">Base directory to search.  All subdirectories will be searched, except directories within other plugins.</param>
 /// <param name="LoadedFrom">Where we're loading these plugins from</param>
 /// <param name="Plugins">List of all of the plugins we found</param>
 public static void FindPluginsIn(string PluginsDirectory, PluginInfo.LoadedFromType LoadedFrom, ref List<PluginInfo> Plugins)
 {
     if (Directory.Exists(PluginsDirectory))
     {
         FindPluginsRecursively(PluginsDirectory, LoadedFrom, ref Plugins);
     }
 }
예제 #3
0
        /// <summary>
        /// Recursively locates all plugins in the specified folder, appending to the incoming list
        /// </summary>
        /// <param name="PluginsDirectory">Directory to search</param>
        /// <param name="LoadedFrom">Where we're loading these plugins from</param>
        /// <param name="Plugins">List of plugins found so far</param>
        private static void FindPluginsRecursively(string PluginsDirectory, PluginInfo.LoadedFromType LoadedFrom, ref List<PluginInfo> Plugins)
        {
            // NOTE: The logic in this function generally matches that of the C++ code for FindPluginsRecursively
            //       in the core engine code.  These routines should be kept in sync.

            // Each sub-directory is possibly a plugin.  If we find that it contains a plugin, we won't recurse any
            // further -- you can't have plugins within plugins.  If we didn't find a plugin, we'll keep recursing.

            var PluginsDirectoryInfo = new DirectoryInfo(PluginsDirectory);
            foreach (var PossiblePluginDirectory in PluginsDirectoryInfo.EnumerateDirectories())
            {
                if (!STBuildTool.IsPathIgnoredForBuild(PossiblePluginDirectory.FullName))
                {
                    // Do we have a plugin descriptor in this directory?
                    bool bFoundPlugin = false;
                    foreach (var PluginDescriptorFileName in Directory.GetFiles(PossiblePluginDirectory.FullName, "*" + PluginDescriptorFileExtension))
                    {
                        // Found a plugin directory!  No need to recurse any further, but make sure it's unique.
                        if (!Plugins.Any(x => x.Directory == PossiblePluginDirectory.FullName))
                        {
                            // Load the plugin info and keep track of it
                            var PluginDescriptorFile = new FileInfo(PluginDescriptorFileName);
                            var PluginInfo = LoadPluginDescriptor(PluginDescriptorFile, LoadedFrom);

                            Plugins.Add(PluginInfo);
                            bFoundPlugin = true;
                            Log.TraceVerbose("Found plugin in: " + PluginInfo.Directory);
                        }

                        // No need to search for more plugins
                        break;
                    }

                    if (!bFoundPlugin)
                    {
                        // Didn't find a plugin in this directory.  Continue to look in subfolders.
                        FindPluginsRecursively(PossiblePluginDirectory.FullName, LoadedFrom, ref Plugins);
                    }
                }
            }
        }
예제 #4
0
        /** Given a UBT-built binary name (e.g. "Core"), returns a relative path to the binary for the current build configuration (e.g. "../Binaries/Win64/Core-Win64-Debug.lib") */
        public static string[] MakeBinaryPaths(string ModuleName, string BinaryName, STTargetPlatform Platform,
            STTargetConfiguration Configuration, STBuildBinaryType BinaryType, TargetRules.TargetType? TargetType, PluginInfo PluginInfo, string AppName, bool bForceNameAsForDevelopment = false, string ExeBinariesSubFolder = null)
        {
            // Determine the binary extension for the platform and binary type.
            var BuildPlatform = STBuildPlatform.GetBuildPlatform(Platform);
            string BinaryExtension;

            if (!BuildConfiguration.bRunUnrealCodeAnalyzer)
            {
                BinaryExtension = BuildPlatform.GetBinaryExtension(BinaryType);
            }
            else
            {
                BinaryExtension = @"-" + BuildConfiguration.UCAModuleToAnalyze + @".analysis";
            }

            STTargetConfiguration LocalConfig = Configuration;
            if (Configuration == STTargetConfiguration.DebugGame && !String.IsNullOrEmpty(ModuleName) && !RulesCompiler.IsGameModule(ModuleName))
            {
                LocalConfig = STTargetConfiguration.Development;
            }

            string ModuleBinariesSubDir = "";
            if (BinaryType == STBuildBinaryType.DynamicLinkLibrary && (string.IsNullOrEmpty(ModuleName) == false))
            {
                // Allow for modules to specify sub-folders in the Binaries folder
                var RulesFilename = RulesCompiler.GetModuleFilename(ModuleName);
                // Plugins can be binary-only and can have no rules object
                if (PluginInfo == null || !String.IsNullOrEmpty(RulesFilename))
                {
                    ModuleRules ModuleRulesObj = RulesCompiler.CreateModuleRules(ModuleName, new TargetInfo(Platform, Configuration, TargetType), out RulesFilename);
                    if (ModuleRulesObj != null)
                    {
                        ModuleBinariesSubDir = ModuleRulesObj.BinariesSubFolder;
                    }
                }
            }
            else if (BinaryType == STBuildBinaryType.Executable && string.IsNullOrEmpty(ExeBinariesSubFolder) == false)
            {
                ModuleBinariesSubDir = ExeBinariesSubFolder;
            }

            //@todo.Rocket: This just happens to work since exp and lib files go into intermediate...

            // Build base directory string ("../Binaries/<Platform>/")
            string BinariesDirName;
            if (TargetType.HasValue && TargetType.Value == TargetRules.TargetType.Program && STBuildTool.HasUProjectFile() && !ProjectFileGenerator.bGenerateProjectFiles)
            {
                BinariesDirName = Path.Combine(STBuildTool.GetUProjectPath(), "Binaries");
            }
            else if (PluginInfo != null)
            {
                BinariesDirName = Path.Combine(PluginInfo.Directory, "Binaries");
            }
            else
            {
                BinariesDirName = Path.Combine("..", "Binaries");
            }
            var BaseDirectory = Path.Combine(BinariesDirName, Platform.ToString());
            if (ModuleBinariesSubDir.Length > 0)
            {
                BaseDirectory = Path.Combine(BaseDirectory, ModuleBinariesSubDir);
            }

            string BinarySuffix = "";
            if ((PluginInfo != null) && (BinaryType != STBuildBinaryType.DynamicLinkLibrary))
            {
                BinarySuffix = "-Static";
            }

            // append the architecture to the end of the binary name
            BinarySuffix = BuildPlatform.ApplyArchitectureName(BinarySuffix);

            string OutBinaryPath = "";
            // Append binary file name
            string Prefix = "";
            if (Platform == STTargetPlatform.Linux && (BinaryType == STBuildBinaryType.DynamicLinkLibrary || BinaryType == STBuildBinaryType.StaticLibrary))
            {
                Prefix = "lib";
            }

            if (LocalConfig == STTargetConfiguration.Development || bForceNameAsForDevelopment)
            {
                OutBinaryPath = Path.Combine(BaseDirectory, String.Format("{3}{0}{1}{2}", BinaryName, BinarySuffix, BinaryExtension, Prefix));
            }
            else
            {
                OutBinaryPath = Path.Combine(BaseDirectory, String.Format("{5}{0}-{1}-{2}{3}{4}",
                    BinaryName, Platform.ToString(), LocalConfig.ToString(), BinarySuffix, BinaryExtension, Prefix));
            }

            return BuildPlatform.FinalizeBinaryPaths(OutBinaryPath);
        }
예제 #5
0
        /// <summary>
        /// Determines whether the given plugin module is part of the current build.
        /// </summary>
        private bool ShouldIncludePluginModule(PluginInfo Plugin, PluginInfo.PluginModuleInfo Module)
        {
            // Check it can be built for this platform...
            if (Module.Platforms.Contains(Platform))
            {
                // ...and that it's appropriate for the current build environment.
                switch (Module.Type)
                {
                    case PluginInfo.PluginModuleType.Runtime:
                    case PluginInfo.PluginModuleType.RuntimeNoCommandlet:
                        return true;

                    case PluginInfo.PluginModuleType.Developer:
                        return STBuildConfiguration.bBuildDeveloperTools;

                    case PluginInfo.PluginModuleType.Editor:
                    case PluginInfo.PluginModuleType.EditorNoCommandlet:
                        return STBuildConfiguration.bBuildEditor;

                    case PluginInfo.PluginModuleType.Program:
                        return TargetType == TargetRules.TargetType.Program;
                }
            }
            return false;
        }
예제 #6
0
 /** Given a UBT-built binary name (e.g. "Core"), returns a relative path to the binary for the current build configuration (e.g. "../../Binaries/Win64/Core-Win64-Debug.lib") */
 public string[] MakeBinaryPaths(string ModuleName, string BinaryName, STBuildBinaryType BinaryType, TargetRules.TargetType? TargetType, PluginInfo PluginInfo, string AppName, bool bForceNameAsForDevelopment = false, string ExeBinariesSubFolder = null)
 {
     if (String.IsNullOrEmpty(ModuleName) && Configuration == STTargetConfiguration.DebugGame && !bCompileMonolithic)
     {
         return MakeBinaryPaths(ModuleName, BinaryName, Platform, STTargetConfiguration.Development, BinaryType, TargetType, PluginInfo, AppName, bForceNameAsForDevelopment);
     }
     else
     {
         return MakeBinaryPaths(ModuleName, BinaryName, Platform, Configuration, BinaryType, TargetType, PluginInfo, AppName, bForceNameAsForDevelopment, ExeBinariesSubFolder);
     }
 }
예제 #7
0
        /// <summary>
        /// Include the given plugin module in the target. Will be built in the appropriate subfolder under the plugin directory.
        /// </summary>
        public List<string> AddPluginModule(PluginInfo Plugin, PluginInfo.PluginModuleInfo Module)
        {
            var SpecialRocketLibFilesThatAreBuildProducts = new List<string>();

            bool bCompileMonolithic = ShouldCompileMonolithic();

            // Get the binary type to build
            STBuildBinaryType BinaryType = bCompileMonolithic ? STBuildBinaryType.StaticLibrary : STBuildBinaryType.DynamicLinkLibrary;

            // Get the output path. Don't prefix the app name for Rocket
            string[] OutputFilePaths;
            if ((STBuildTool.BuildingRocket() || STBuildTool.RunningRocket()) && bCompileMonolithic)
            {
                OutputFilePaths = MakeBinaryPaths(Module.Name, Module.Name, BinaryType, TargetType, Plugin, AppName);
                if (STBuildTool.BuildingRocket())
                {
                    SpecialRocketLibFilesThatAreBuildProducts.AddRange(OutputFilePaths);
                }
            }
            else
            {
                OutputFilePaths = MakeBinaryPaths(Module.Name, GetAppName() + "-" + Module.Name, BinaryType, TargetType, Plugin, AppName);
            }

            // Try to determine if we have the rules file
            var ModuleFilename = RulesCompiler.GetModuleFilename(Module.Name);
            var bHasModuleRules = String.IsNullOrEmpty(ModuleFilename) == false;

            // Figure out whether we should build it from source
            var ModuleSourceFolder = bHasModuleRules ? Path.GetDirectoryName(RulesCompiler.GetModuleFilename(Module.Name)) : ModuleFilename;
            bool bShouldBeBuiltFromSource = bHasModuleRules && Directory.GetFiles(ModuleSourceFolder, "*.cpp", SearchOption.AllDirectories).Length > 0;

            string PluginIntermediateBuildPath;
            {
                if (Plugin.LoadedFrom == PluginInfo.LoadedFromType.Engine)
                {
                    // Plugin folder is in the engine directory
                    var PluginConfiguration = Configuration == STTargetConfiguration.DebugGame ? STTargetConfiguration.Development : Configuration;
                    PluginIntermediateBuildPath = Path.GetFullPath(Path.Combine(BuildConfiguration.RelativeEnginePath, BuildConfiguration.PlatformIntermediateFolder, AppName, PluginConfiguration.ToString()));
                }
                else
                {
                    // Plugin folder is in the project directory
                    PluginIntermediateBuildPath = Path.GetFullPath(Path.Combine(ProjectDirectory, BuildConfiguration.PlatformIntermediateFolder, GetTargetName(), Configuration.ToString()));
                }
                PluginIntermediateBuildPath = Path.Combine(PluginIntermediateBuildPath, "Plugins", ShouldCompileMonolithic() ? "Static" : "Dynamic");
            }

            // Create the binary
            STBuildBinaryConfiguration Config = new STBuildBinaryConfiguration(InType: BinaryType,
                                                                                InOutputFilePaths: OutputFilePaths,
                                                                                InIntermediateDirectory: PluginIntermediateBuildPath,
                                                                                bInAllowExports: true,
                                                                                bInAllowCompilation: bShouldBeBuiltFromSource,
                                                                                bInHasModuleRules: bHasModuleRules,
                                                                                InModuleNames: new List<string> { Module.Name });
            AppBinaries.Add(new STBuildBinaryCPP(this, Config));
            return SpecialRocketLibFilesThatAreBuildProducts;
        }
예제 #8
0
 /// <summary>
 /// Include the given plugin in the target. It may be included as a separate binary, or compiled into a monolithic executable.
 /// </summary>
 public List<string> AddPlugin(PluginInfo Plugin)
 {
     var SpecialRocketLibFilesThatAreBuildProducts = new List<string>();
     foreach (PluginInfo.PluginModuleInfo Module in Plugin.Modules)
     {
         if (ShouldIncludePluginModule(Plugin, Module))
         {
             SpecialRocketLibFilesThatAreBuildProducts.AddRange(AddPluginModule(Plugin, Module));
         }
     }
     return SpecialRocketLibFilesThatAreBuildProducts;
 }