Exemplo n.º 1
0
        private BuildResultCode BuildMaster()
        {
            try
            {
                PackageSessionPublicHelper.FindAndSetMSBuildVersion();
            }
            catch (Exception e)
            {
                var message = "Could not find a compatible version of MSBuild.\r\n\r\n" +
                              "Check that you have a valid installation with the required workloads, or go to [www.visualstudio.com/downloads](https://www.visualstudio.com/downloads) to install a new one.\r\n\r\n" +
                              e;
                builderOptions.Logger.Error(message);
                return(BuildResultCode.BuildError);
            }

            AssetCompilerContext context        = null;
            PackageSession       projectSession = null;

            try
            {
                var sessionLoadParameters = new PackageLoadParameters
                {
                    AutoCompileProjects     = !builderOptions.DisableAutoCompileProjects,
                    ExtraCompileProperties  = builderOptions.ExtraCompileProperties,
                    RemoveUnloadableObjects = true,
                    BuildConfiguration      = builderOptions.ProjectConfiguration,
                };

                // Loads the root Package
                var projectSessionResult = PackageSession.Load(builderOptions.PackageFile, sessionLoadParameters);
                projectSessionResult.CopyTo(builderOptions.Logger);
                if (projectSessionResult.HasErrors)
                {
                    return(BuildResultCode.BuildError);
                }

                projectSession = projectSessionResult.Session;

                // Find loaded package (either sdpkg or csproj) -- otherwise fallback to first one
                var packageFile = (UFile)builderOptions.PackageFile;
                var package     = projectSession.Packages.FirstOrDefault(x => x.FullPath == packageFile || (x.Container is SolutionProject project && project.FullPath == packageFile))
                                  ?? projectSession.LocalPackages.FirstOrDefault()
                                  ?? projectSession.Packages.FirstOrDefault();

                // Setup variables
                var buildDirectory  = builderOptions.BuildDirectory;
                var outputDirectory = builderOptions.OutputDirectory;

                // Process game settings asset
                var gameSettingsAsset = package.GetGameSettingsAsset();
                if (gameSettingsAsset == null)
                {
                    builderOptions.Logger.Warning($"Could not find game settings asset at location [{GameSettingsAsset.GameSettingsLocation}]. Use a Default One");
                    gameSettingsAsset = GameSettingsFactory.Create();
                }

                // Create context
                context = new AssetCompilerContext
                {
                    Platform           = builderOptions.Platform,
                    CompilationContext = typeof(AssetCompilationContext),
                    BuildConfiguration = builderOptions.ProjectConfiguration,
                    Package            = package,
                };

                // Command line properties
                foreach (var property in builderOptions.Properties)
                {
                    context.OptionProperties.Add(property.Key, property.Value);
                }

                // Set current game settings
                context.SetGameSettingsAsset(gameSettingsAsset);

                // Builds the project
                var assetBuilder = new PackageCompiler(new RootPackageAssetEnumerator(package));
                assetBuilder.AssetCompiled += RegisterBuildStepProcessedHandler;

                context.Properties.Set(BuildAssetNode.VisitRuntimeTypes, true);
                var assetBuildResult = assetBuilder.Prepare(context);
                assetBuildResult.CopyTo(builderOptions.Logger);
                if (assetBuildResult.HasErrors)
                {
                    return(BuildResultCode.BuildError);
                }

                // Setup the remote process build
                var remoteBuilderHelper = new PackageBuilderRemoteHelper(projectSession.AssemblyContainer, builderOptions);

                var indexName = $"index.{package.Meta.Name}.{builderOptions.Platform}";
                // Add runtime identifier (if any) to avoid clash when building multiple at the same time (this happens when using ExtrasBuildEachRuntimeIdentifier feature of MSBuild.Sdk.Extras)
                if (builderOptions.Properties.TryGetValue("RuntimeIdentifier", out var runtimeIdentifier))
                {
                    indexName += $".{runtimeIdentifier}";
                }
                if (builderOptions.ExtraCompileProperties != null && builderOptions.ExtraCompileProperties.TryGetValue("StrideGraphicsApi", out var graphicsApi))
                {
                    indexName += $".{graphicsApi}";
                }

                // Create the builder
                builder = new Builder(builderOptions.Logger, buildDirectory, indexName)
                {
                    ThreadCount = builderOptions.ThreadCount, TryExecuteRemote = remoteBuilderHelper.TryExecuteRemote
                };

                builder.MonitorPipeNames.AddRange(builderOptions.MonitorPipeNames);

                // Add build steps generated by AssetBuilder
                builder.Root.Add(assetBuildResult.BuildSteps);

                // Run builder
                var result = builder.Run(Builder.Mode.Build);
                builder.WriteIndexFile(false);

                // Fill list of bundles
                var bundlePacker = new BundlePacker();
                var bundleFiles  = new List <string>();
                bundlePacker.Build(builderOptions.Logger, projectSession, package, indexName, outputDirectory, builder.DisableCompressionIds, context.GetCompilationMode() != CompilationMode.AppStore, bundleFiles);

                if (builderOptions.MSBuildUpToDateCheckFileBase != null)
                {
                    SaveBuildUpToDateFile(builderOptions.MSBuildUpToDateCheckFileBase, package, bundleFiles);
                }

                return(result);
            }
            finally
            {
                builder?.Dispose();
                // Dispose the session (in order to unload assemblies)
                projectSession?.Dispose();
                context?.Dispose();

                // Make sure that MSBuild doesn't hold anything else
                VSProjectHelper.Reset();
            }
        }
Exemplo n.º 2
0
        public int Run(string[] args)
        {
            // This is used by ExecServer to retrieve the logs directly without using the console redirect (which is not working well
            // in a multi-domain scenario)
            var redirectLogToAppDomainAction = AppDomain.CurrentDomain.GetData("AppDomainLogToAction") as Action <string, ConsoleColor>;

            clock = Stopwatch.StartNew();

            // TODO this is hardcoded. Check how to make this dynamic instead.
            RuntimeHelpers.RunModuleConstructor(typeof(IProceduralModel).Module.ModuleHandle);
            RuntimeHelpers.RunModuleConstructor(typeof(MaterialKeys).Module.ModuleHandle);
            RuntimeHelpers.RunModuleConstructor(typeof(SpriteFontAsset).Module.ModuleHandle);
            RuntimeHelpers.RunModuleConstructor(typeof(ModelAsset).Module.ModuleHandle);
            RuntimeHelpers.RunModuleConstructor(typeof(SpriteStudioAnimationAsset).Module.ModuleHandle);
            //var project = new Package();
            //project.Save("test.pdxpkg");

            //Thread.Sleep(10000);
            //var spriteFontAsset = StaticFontAsset.New();
            //Asset.Save("test.pdxfnt", spriteFontAsset);
            //project.Refresh();

            //args = new string[] { "test.pdxpkg", "-o:app_data", "-b:tmp", "-t:1" };

            var exeName           = Path.GetFileName(Assembly.GetExecutingAssembly().Location);
            var showHelp          = false;
            var buildEngineLogger = GlobalLogger.GetLogger("BuildEngine");
            var options           = new PackageBuilderOptions(new ForwardingLoggerResult(buildEngineLogger));

            var p = new OptionSet
            {
                "Copyright (C) 2011-2014 Silicon Studio Corporation. All Rights Reserved",
                "Paradox Build Tool - Version: "
                +
                String.Format(
                    "{0}.{1}.{2}",
                    typeof(Program).Assembly.GetName().Version.Major,
                    typeof(Program).Assembly.GetName().Version.Minor,
                    typeof(Program).Assembly.GetName().Version.Build) + string.Empty,
                string.Format("Usage: {0} inputPackageFile [options]* -b buildPath", exeName),
                string.Empty,
                "=== Options ===",
                string.Empty,
                { "h|help", "Show this message and exit", v => showHelp = v != null },
                { "v|verbose", "Show more verbose progress logs", v => options.Verbose = v != null },
                { "d|debug", "Show debug logs (imply verbose)", v => options.Debug = v != null },
                { "log", "Enable file logging", v => options.EnableFileLogging = v != null },
                { "disable-auto-compile", "Disable auto-compile of projects", v => options.DisableAutoCompileProjects = v != null },
                { "p|profile=", "Profile name", v => options.BuildProfile = v },
                { "project-configuration=", "Project configuration", v => options.ProjectConfiguration = v },
                { "platform=", "Platform name", v => options.Platform = (PlatformType)Enum.Parse(typeof(PlatformType), v) },
                { "graphics-platform=", "Graphics Platform name", v => options.GraphicsPlatform = (GraphicsPlatform)Enum.Parse(typeof(GraphicsPlatform), v) },
                { "get-graphics-platform", "Get Graphics Platform name (needs a pdxpkg and a profile)", v => options.GetGraphicsPlatform = v != null },
                { "solution-file=", "Solution File Name", v => options.SolutionFile = v },
                { "package-id=", "Package Id from the solution file", v => options.PackageId = Guid.Parse(v) },
                { "package-file=", "Input Package File Name", v => options.PackageFile = v },
                { "o|output-path=", "Output path", v => options.OutputDirectory = v },
                { "b|build-path=", "Build path", v => options.BuildDirectory = v },
                { "log-file=", "Log build in a custom file.", v =>
                  {
                      options.EnableFileLogging = v != null;
                      options.CustomLogFileName = v;
                  } },
                { "log-pipe=", "Log pipe.", v =>
                  {
                      if (!string.IsNullOrEmpty(v))
                      {
                          options.LogPipeNames.Add(v);
                      }
                  } },
                { "monitor-pipe=", "Monitor pipe.", v =>
                  {
                      if (!string.IsNullOrEmpty(v))
                      {
                          options.MonitorPipeNames.Add(v);
                      }
                  } },
                { "slave=", "Slave pipe", v => options.SlavePipe = v }, // Benlitz: I don't think this should be documented
                { "server=", "This Compiler is launched as a server", v => { } },
                { "t|threads=", "Number of threads to create. Default value is the number of hardware threads available.", v => options.ThreadCount = int.Parse(v) },
                { "test=", "Run a test session.", v => options.TestName = v },
                { "property:", "Properties. Format is name1=value1;name2=value2", v =>
                  {
                      if (!string.IsNullOrEmpty(v))
                      {
                          foreach (var nameValue in v.Split(new [] { ';' }, StringSplitOptions.RemoveEmptyEntries))
                          {
                              var equalIndex = nameValue.IndexOf('=');
                              if (equalIndex == -1)
                              {
                                  throw new OptionException("Expect name1=value1;name2=value2 format.", "property");
                              }

                              options.Properties.Add(nameValue.Substring(0, equalIndex), nameValue.Substring(equalIndex + 1));
                          }
                      }
                  } },
                { "compile-property:", "Compile properties. Format is name1=value1;name2=value2", v =>
                  {
                      if (!string.IsNullOrEmpty(v))
                      {
                          if (options.ExtraCompileProperties == null)
                          {
                              options.ExtraCompileProperties = new Dictionary <string, string>();
                          }

                          foreach (var nameValue in v.Split(new [] { ';' }, StringSplitOptions.RemoveEmptyEntries))
                          {
                              var equalIndex = nameValue.IndexOf('=');
                              if (equalIndex == -1)
                              {
                                  throw new OptionException("Expect name1=value1;name2=value2 format.", "property");
                              }

                              options.ExtraCompileProperties.Add(nameValue.Substring(0, equalIndex), nameValue.Substring(equalIndex + 1));
                          }
                      }
                  } },
            };

            TextWriterLogListener fileLogListener = null;

            BuildResultCode exitCode;

            try
            {
                var unexpectedArgs = p.Parse(args);

                // Activate proper log level
                buildEngineLogger.ActivateLog(options.LoggerType);

                // Output logs to the console with colored messages
                if (options.SlavePipe == null && !options.LogPipeNames.Any())
                {
                    if (redirectLogToAppDomainAction != null)
                    {
                        globalLoggerOnGlobalMessageLogged = new LogListenerRedirectToAction(redirectLogToAppDomainAction);
                    }
                    else
                    {
                        globalLoggerOnGlobalMessageLogged = new ConsoleLogListener {
                            LogMode = ConsoleLogMode.Always
                        };
                    }
                    globalLoggerOnGlobalMessageLogged.TextFormatter = FormatLog;
                    GlobalLogger.GlobalMessageLogged += globalLoggerOnGlobalMessageLogged;
                }

                if (unexpectedArgs.Any())
                {
                    throw new OptionException("Unexpected arguments [{0}]".ToFormat(string.Join(", ", unexpectedArgs)), "args");
                }
                try
                {
                    options.ValidateOptions();
                }
                catch (ArgumentException ex)
                {
                    throw new OptionException(ex.Message, ex.ParamName);
                }

                // Also write logs from master process into a file
                if (options.SlavePipe == null)
                {
                    if (options.EnableFileLogging)
                    {
                        string logFileName = options.CustomLogFileName;
                        if (string.IsNullOrEmpty(logFileName))
                        {
                            string inputName = Path.GetFileNameWithoutExtension(options.PackageFile);
                            logFileName = "Logs/Build-" + inputName + "-" + DateTime.Now.ToString("yy-MM-dd-HH-mm") + ".txt";
                        }

                        string dirName = Path.GetDirectoryName(logFileName);
                        if (dirName != null)
                        {
                            Directory.CreateDirectory(dirName);
                        }

                        fileLogListener = new TextWriterLogListener(new FileStream(logFileName, FileMode.Create))
                        {
                            TextFormatter = FormatLog
                        };
                        GlobalLogger.GlobalMessageLogged += fileLogListener;
                    }
                    if (!options.GetGraphicsPlatform)
                    {
                        options.Logger.Info("BuildEngine arguments: " + string.Join(" ", args));
                        options.Logger.Info("Starting builder.");
                    }
                }
                else
                {
                    IsSlave = true;
                }

                if (showHelp)
                {
                    p.WriteOptionDescriptions(Console.Out);
                    exitCode = BuildResultCode.Successful;
                }
                else if (!string.IsNullOrEmpty(options.TestName))
                {
                    var test = new TestSession();
                    test.RunTest(options.TestName, options.Logger);
                    exitCode = BuildResultCode.Successful;
                }
                else
                {
                    builder = new PackageBuilder(options);
                    if (!IsSlave && redirectLogToAppDomainAction == null)
                    {
                        Console.CancelKeyPress += OnConsoleOnCancelKeyPress;
                    }
                    exitCode = builder.Build();
                }
            }
            catch (OptionException e)
            {
                options.Logger.Error("Command option '{0}': {1}", e.OptionName, e.Message);
                exitCode = BuildResultCode.CommandLineError;
            }
            catch (Exception e)
            {
                options.Logger.Error("Unhandled exception: {0}", e, e.Message);
                exitCode = BuildResultCode.BuildError;
            }
            finally
            {
                if (fileLogListener != null)
                {
                    GlobalLogger.GlobalMessageLogged -= fileLogListener;
                    fileLogListener.LogWriter.Close();
                }

                // Output logs to the console with colored messages
                if (globalLoggerOnGlobalMessageLogged != null)
                {
                    GlobalLogger.GlobalMessageLogged -= globalLoggerOnGlobalMessageLogged;
                }
                if (builder != null && !IsSlave && redirectLogToAppDomainAction == null)
                {
                    Console.CancelKeyPress -= OnConsoleOnCancelKeyPress;
                }

                // Make sure that MSBuild doesn't hold anything else
                VSProjectHelper.Reset();

                // Reset cache hold by YamlSerializer
                YamlSerializer.ResetCache();
            }
            return((int)exitCode);
        }
Exemplo n.º 3
0
        protected override bool Generate(SessionTemplateGeneratorParameters parameters)
        {
            if (parameters == null)
            {
                throw new ArgumentNullException(nameof(parameters));
            }
            parameters.Validate();

            var description = (TemplateSampleDescription)parameters.Description;
            var log         = parameters.Logger;

            // The package might depend on other packages which need to be copied together when instanciating it.
            //  However some of these packages might be in a parent folder, which can result in undesired behavior when copied.
            //  Setting this to true will enforce all package dependencies to be moved to a folder local to the project
            bool doMoveParentDependencies = true;

            var regexes     = new List <Tuple <Regex, MatchEvaluator> >();
            var patternName = description.PatternName ?? description.DefaultOutputName;

            // Samples don't support spaces and dot in name (we would need to separate package name, package short name and namespace renaming for that).
            var parametersName = parameters.Name.Replace(" ", string.Empty).Replace(".", string.Empty);

            if (patternName != parametersName)
            {
                // Make sure the target name is a safe for use everywhere, since both an asset or script might reference a filename
                //  in which case they should match and be valid in that context
                string validNamespaceName = Utilities.BuildValidNamespaceName(parametersName);

                // Rename for general occurences of template name
                regexes.Add(new Tuple <Regex, MatchEvaluator>(new Regex($@"\b{patternName}\b"), match => validNamespaceName));

                // Rename App as well (used in code) -- this is the only pattern of "package short name" that we have so far in Windows samples
                regexes.Add(new Tuple <Regex, MatchEvaluator>(new Regex($@"\b{patternName}App\b"), match => validNamespaceName));
            }

            var outputDirectory = parameters.OutputDirectory;

            if (!Directory.Exists(outputDirectory))
            {
                Directory.CreateDirectory(outputDirectory);
            }

            //write gitignore
            WriteGitIgnore(parameters);

            UFile projectOutputFile = null;
            UFile projectInputFile  = null;

            // Process files
            foreach (var directory in FileUtility.EnumerateDirectories(description.TemplateDirectory, SearchDirection.Down))
            {
                foreach (var file in directory.GetFiles())
                {
                    // If the file is ending with the Template extension or a directory with the sample extension, don;t copy it
                    if (file.FullName.EndsWith(TemplateDescription.FileExtension) ||
                        string.Compare(directory.Name, TemplateDescription.FileExtension, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        continue;
                    }

                    var relativeFile = new UFile(file.FullName).MakeRelative(description.TemplateDirectory);

                    // Replace the name in the files if necessary
                    foreach (var nameRegex in regexes)
                    {
                        relativeFile = nameRegex.Item1.Replace(relativeFile, nameRegex.Item2);
                    }

                    // Create the output directory if needed
                    var outputFile          = UPath.Combine(outputDirectory, relativeFile);
                    var outputFileDirectory = outputFile.GetParent();

                    // Determine if we are processing the main game project
                    var isPackageFile = (projectOutputFile == null && Path.GetExtension(file.FullName).ToLowerInvariant() == ".csproj" && !Path.GetFileNameWithoutExtension(file.FullName).EndsWith(".Windows"));

                    if (isPackageFile)
                    {
                        projectInputFile  = file.FullName;
                        projectOutputFile = outputFile;
                    }

                    if (!Directory.Exists(outputFileDirectory))
                    {
                        Directory.CreateDirectory(outputFileDirectory);
                    }

                    if (IsBinaryFile(file.FullName))
                    {
                        File.Copy(file.FullName, outputFile, true);
                    }
                    else
                    {
                        ProcessTextFile(file.FullName, outputFile, regexes, (isPackageFile && doMoveParentDependencies));
                    }
                }
            }

            if (projectOutputFile != null)
            {
                var inputProject   = (SolutionProject)Package.LoadProject(log, projectInputFile);
                var outputProject  = (SolutionProject)Package.LoadProject(log, projectOutputFile);
                var msbuildProject = VSProjectHelper.LoadProject(outputProject.FullPath, platform: "NoPlatform");

                // If requested, add reference to Xenko.Games.Testing
                if (parameters.TryGetTag(AddGamesTestingKey))
                {
                    var items = msbuildProject.AddItem("PackageReference", "Xenko.Games.Testing", new[] { new KeyValuePair <string, string>("Version", XenkoVersion.NuGetVersion), new KeyValuePair <string, string>("PrivateAssets", "contentfiles;analyzers") });
                    foreach (var item in items)
                    {
                        foreach (var metadata in item.Metadata)
                        {
                            metadata.Xml.ExpressedAsAttribute = true;
                        }
                    }
                }

                // Copy dependency files locally
                //  We only want to copy the asset files. The raw files are in Resources and the game assets are in Assets.
                //  If we copy each file locally they will be included in the package and we can then delete the dependency packages.
                foreach (var projectReference in msbuildProject.GetItems("ProjectReference").ToList())
                {
                    var packageDirectory = UPath.Combine(inputProject.FullPath.GetFullDirectory(), (UFile)projectReference.EvaluatedInclude).GetFullDirectory();
                    foreach (var directory in FileUtility.EnumerateDirectories(packageDirectory, SearchDirection.Down))
                    {
                        foreach (var file in directory.GetFiles())
                        {
                            // If the file is ending with the Template extension or a directory with the sample extension, don`t copy it
                            if (file.FullName.EndsWith(TemplateDescription.FileExtension) ||
                                string.Compare(directory.Name, TemplateDescription.FileExtension, StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                continue;
                            }

                            var relativeFile     = new UFile(file.FullName).MakeRelative(packageDirectory);
                            var relativeFilename = relativeFile.ToString();

                            bool isAsset    = relativeFilename.Contains("Assets");
                            bool isResource = relativeFilename.Contains("Resources");

                            if (!isAsset && !isResource)
                            {
                                continue;
                            }

                            // Replace the name in the files if necessary
                            foreach (var nameRegex in regexes)
                            {
                                relativeFile = nameRegex.Item1.Replace(relativeFile, nameRegex.Item2);
                            }

                            var outputFile = UPath.Combine(outputDirectory, relativeFile);
                            {   // Create the output directory if needed
                                var outputFileDirectory = outputFile.GetParent();
                                if (!Directory.Exists(outputFileDirectory))
                                {
                                    Directory.CreateDirectory(outputFileDirectory);
                                }
                            }

                            if (IsBinaryFile(file.FullName))
                            {
                                File.Copy(file.FullName, outputFile, true);
                            }
                            else
                            {
                                ProcessTextFile(file.FullName, outputFile, regexes);
                            }
                        }
                    }

                    msbuildProject.RemoveItem(projectReference);
                }

                // Save csproj without ProjectReferences
                msbuildProject.Save();
                msbuildProject.ProjectCollection.UnloadAllProjects();
                msbuildProject.ProjectCollection.Dispose();

                // Add package to session
                var loadParams = PackageLoadParameters.Default();
                loadParams.GenerateNewAssetIds     = true;
                loadParams.LoadMissingDependencies = false;
                var session = parameters.Session;
                // We should switch to loading .csproj once all samples are upgraded
                var loadedProject = session.AddExistingProject(projectOutputFile, log, loadParams);

                RemoveUnusedAssets(loadedProject.Package, session);

                parameters.Tags.Add(GeneratedPackageKey, loadedProject.Package);
            }
            else
            {
                log.Error("Unable to find generated package for this template");
            }

            // Make sure we transfer overrides, etc. from what we deserialized to the asset graphs that we are going to save right after.
            ApplyMetadata(parameters);
            return(true);
        }
Exemplo n.º 4
0
        private BuildResultCode BuildMaster()
        {
            try
            {
                PackageSessionPublicHelper.FindAndSetMSBuildVersion();
            }
            catch (Exception e)
            {
                var message = "Could not find a compatible version of MSBuild.\r\n\r\n" +
                              "Check that you have a valid installation with the required workloads, or go to [www.visualstudio.com/downloads](https://www.visualstudio.com/downloads) to install a new one.\r\n\r\n" +
                              e;
                builderOptions.Logger.Error(message);
                return(BuildResultCode.BuildError);
            }

            AssetCompilerContext context        = null;
            PackageSession       projectSession = null;

            try
            {
                var sessionLoadParameters = new PackageLoadParameters
                {
                    AutoCompileProjects     = !builderOptions.DisableAutoCompileProjects,
                    ExtraCompileProperties  = builderOptions.ExtraCompileProperties,
                    RemoveUnloadableObjects = true,
                    BuildConfiguration      = builderOptions.ProjectConfiguration,
                };

                // Loads the root Package
                var projectSessionResult = PackageSession.Load(builderOptions.PackageFile, sessionLoadParameters);
                projectSessionResult.CopyTo(builderOptions.Logger);
                if (projectSessionResult.HasErrors)
                {
                    return(BuildResultCode.BuildError);
                }

                projectSession = projectSessionResult.Session;

                // Check build configuration
                var package = projectSession.LocalPackages.Last();

                // Setup variables
                var buildDirectory  = builderOptions.BuildDirectory;
                var outputDirectory = builderOptions.OutputDirectory;

                // Process game settings asset
                var gameSettingsAsset = package.GetGameSettingsAsset();
                if (gameSettingsAsset == null)
                {
                    builderOptions.Logger.Warning($"Could not find game settings asset at location [{GameSettingsAsset.GameSettingsLocation}]. Use a Default One");
                    gameSettingsAsset = GameSettingsFactory.Create();
                }

                // Create context
                context = new AssetCompilerContext
                {
                    Platform           = builderOptions.Platform,
                    CompilationContext = typeof(AssetCompilationContext),
                    BuildConfiguration = builderOptions.ProjectConfiguration
                };

                // Command line properties
                foreach (var property in builderOptions.Properties)
                {
                    context.OptionProperties.Add(property.Key, property.Value);
                }

                // Set current game settings
                context.SetGameSettingsAsset(gameSettingsAsset);

                // Builds the project
                var assetBuilder = new PackageCompiler(new RootPackageAssetEnumerator(package));
                assetBuilder.AssetCompiled += RegisterBuildStepProcessedHandler;

                context.Properties.Set(BuildAssetNode.VisitRuntimeTypes, true);
                var assetBuildResult = assetBuilder.Prepare(context);
                assetBuildResult.CopyTo(builderOptions.Logger);
                if (assetBuildResult.HasErrors)
                {
                    return(BuildResultCode.BuildError);
                }

                // Setup the remote process build
                var remoteBuilderHelper = new PackageBuilderRemoteHelper(projectSession.AssemblyContainer, builderOptions);

                // Create the builder
                var indexName = "index." + package.Meta.Name;
                builder = new Builder(builderOptions.Logger, buildDirectory, indexName)
                {
                    ThreadCount = builderOptions.ThreadCount, TryExecuteRemote = remoteBuilderHelper.TryExecuteRemote
                };

                builder.MonitorPipeNames.AddRange(builderOptions.MonitorPipeNames);

                // Add build steps generated by AssetBuilder
                builder.Root.Add(assetBuildResult.BuildSteps);

                // Run builder
                var result = builder.Run(Builder.Mode.Build);
                builder.WriteIndexFile(false);

                // Fill list of bundles
                var bundlePacker = new BundlePacker();
                bundlePacker.Build(builderOptions.Logger, projectSession, indexName, outputDirectory, builder.DisableCompressionIds, context.GetCompilationMode() != CompilationMode.AppStore);

                return(result);
            }
            finally
            {
                builder?.Dispose();
                // Dispose the session (in order to unload assemblies)
                projectSession?.Dispose();
                context?.Dispose();

                // Make sure that MSBuild doesn't hold anything else
                VSProjectHelper.Reset();
            }
        }
        /// <summary>
        /// Determines whether [is reference project] [the specified file path].
        /// </summary>
        /// <param name="filePath">The file path.</param>
        /// <param name="assemblyName">The search pattern.</param>
        /// <returns><c>true</c> if [is reference project] [the specified file path]; otherwise, <c>false</c>.</returns>
        private bool IsReferenceProject(string filePath, string assemblyName)
        {
            var result = !string.IsNullOrEmpty(VSProjectHelper.GetReferenceVersion(filePath, assemblyName));

            return(result);
        }
Exemplo n.º 6
0
        public async Task <UpdateResult> Recompile(Project gameProject, LoggerResult logger)
        {
            var result = new UpdateResult(logger);

            if (solution == null)
            {
                solution = gameProject.Solution;
            }

            // Detect new groups
            var gameProjectCompilation = await gameProject.GetCompilationAsync();

            // Generate source dependency graph
            var sourceDependencyGraph = new SourceGroup();

            // Make sure all vertices are added
            foreach (var syntaxTree in gameProjectCompilation.SyntaxTrees)
            {
                sourceDependencyGraph.AddVertex(syntaxTree);
            }

            foreach (var syntaxTree in gameProjectCompilation.SyntaxTrees)
            {
                var syntaxRoot    = syntaxTree.GetRoot();
                var semanticModel = gameProjectCompilation.GetSemanticModel(syntaxTree);

                var dependencies = new SourceDependencySyntaxVisitor(new HashSet <SyntaxTree>(gameProjectCompilation.SyntaxTrees), semanticModel).DefaultVisit(syntaxRoot);

                foreach (var dependency in dependencies)
                {
                    sourceDependencyGraph.AddEdge(new SEdge <SyntaxTree>(syntaxTree, dependency));
                }
            }

            // Generate strongly connected components for sources (group of sources that needs to be compiled together, and their dependencies)
            var stronglyConnected     = sourceDependencyGraph.CondensateStronglyConnected <SyntaxTree, SEdge <SyntaxTree>, SourceGroup>();
            var sortedConnectedGroups = stronglyConnected.TopologicalSort().ToArray();
            var connectedGroups       = ImmutableHashSet.Create(SourceGroupComparer.Default, sortedConnectedGroups);

            // Merge changes since previous time
            // 1. Tag obsolete groups (everything that don't match, and their dependencies)
            var groupsToUnload = new HashSet <SourceGroup>(SourceGroupComparer.Default);

            foreach (var sourceGroup in previousSortedConnectedGroups.Reverse())
            {
                // Does this group needs reload?
                SourceGroup newSourceGroup;
                if (connectedGroups.TryGetValue(sourceGroup, out newSourceGroup))
                {
                    // Transfer project, as it can be reused
                    newSourceGroup.Project  = sourceGroup.Project;
                    newSourceGroup.PE       = sourceGroup.PE;
                    newSourceGroup.PDB      = sourceGroup.PDB;
                    newSourceGroup.Assembly = sourceGroup.Assembly;
                }
                else
                {
                    groupsToUnload.Add(sourceGroup);
                }

                // Mark dependencies
                if (groupsToUnload.Contains(sourceGroup))
                {
                    foreach (var test in previousStronglyConnected.InEdges(sourceGroup))
                    {
                        groupsToUnload.Add(test.Source);
                    }
                }
            }

            // Generate common InternalsVisibleTo attributes
            // TODO: Find more graceful solution
            var internalsVisibleToBuilder = new StringBuilder();

            internalsVisibleToBuilder.Append("using System.Runtime.CompilerServices;");

            for (int i = 0; i < 1000; i++)
            {
                internalsVisibleToBuilder.AppendFormat(@"[assembly: InternalsVisibleTo(""{0}.Part{1}"")]", gameProject.AssemblyName, i);
            }

            var internalsVisibleToSource = CSharpSyntaxTree.ParseText(internalsVisibleToBuilder.ToString(), null, "", Encoding.UTF8).GetText();

            // 2. Compile assemblies
            foreach (var sourceGroup in sortedConnectedGroups.Reverse())
            {
                // Check if it's either a new group, or one that has been unloaded
                if (!previousConnectedGroups.Contains(sourceGroup) || groupsToUnload.Contains(sourceGroup))
                {
                    var assemblyName = gameProject.AssemblyName + ".Part" + assemblyCounter++;

                    // Create a project out of the source group
                    var project = solution.AddProject(assemblyName, assemblyName, LanguageNames.CSharp)
                                  .WithMetadataReferences(gameProject.MetadataReferences)
                                  .WithProjectReferences(gameProject.AllProjectReferences)
                                  .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

                    // Add sources
                    foreach (var syntaxTree in sourceGroup.Vertices)
                    {
                        project = project.AddDocument(syntaxTree.FilePath, syntaxTree.GetText()).Project;
                    }

                    // Add references to other sources
                    foreach (var dependencySourceGroup in stronglyConnected.OutEdges(sourceGroup))
                    {
                        project = project.AddProjectReference(new ProjectReference(dependencySourceGroup.Target.Project.Id));
                    }

                    // Make internals visible to other assembly parts
                    project = project.AddDocument("GeneratedInternalsVisibleTo", internalsVisibleToSource).Project;

                    sourceGroup.Project = project;
                    solution            = project.Solution;

                    var compilation = await project.GetCompilationAsync();

                    using (var peStream = new MemoryStream())
                        using (var pdbStream = new MemoryStream())
                        {
                            var emitResult = compilation.Emit(peStream, pdbStream);
                            result.Info($"Compiling assembly containing {sourceGroup}");

                            foreach (var diagnostic in emitResult.Diagnostics)
                            {
                                switch (diagnostic.Severity)
                                {
                                case DiagnosticSeverity.Error:
                                    result.Error(diagnostic.GetMessage());
                                    break;

                                case DiagnosticSeverity.Warning:
                                    result.Warning(diagnostic.GetMessage());
                                    break;

                                case DiagnosticSeverity.Info:
                                    result.Info(diagnostic.GetMessage());
                                    break;
                                }
                            }

                            if (!emitResult.Success)
                            {
                                result.Error($"Error compiling assembly containing {sourceGroup}");
                                break;
                            }

                            // Load csproj to evaluate assembly processor parameters
                            var msbuildProject = await Task.Run(() => VSProjectHelper.LoadProject(gameProject.FilePath));

                            if (msbuildProject.GetPropertyValue("StrideAssemblyProcessor") == "true")
                            {
                                var referenceBuild = await Task.Run(() => VSProjectHelper.CompileProjectAssemblyAsync(null, gameProject.FilePath, result, "ResolveReferences", flags: Microsoft.Build.Execution.BuildRequestDataFlags.ProvideProjectStateAfterBuild));

                                if (referenceBuild == null)
                                {
                                    result.Error("Could not properly run ResolveAssemblyReferences");
                                    break;
                                }
                                var referenceBuildResult = await referenceBuild.BuildTask;
                                if (referenceBuild.IsCanceled || result.HasErrors)
                                {
                                    break;
                                }

                                var assemblyProcessorParameters = "--parameter-key --auto-module-initializer --serialization";
                                var assemblyProcessorApp        = AssemblyProcessorProgram.CreateAssemblyProcessorApp(SplitCommandLine(assemblyProcessorParameters).ToArray(), new LoggerAssemblyProcessorWrapper(result));

                                foreach (var referencePath in referenceBuildResult.ProjectStateAfterBuild.Items.Where(x => x.ItemType == "ReferencePath"))
                                {
                                    assemblyProcessorApp.References.Add(referencePath.EvaluatedInclude);
                                    if (referencePath.EvaluatedInclude.EndsWith("Stride.SpriteStudio.Runtime.dll")) //todo hard-coded! needs to go when plug in system is in
                                    {
                                        assemblyProcessorApp.ReferencesToAdd.Add(referencePath.EvaluatedInclude);
                                    }
                                    else if (referencePath.EvaluatedInclude.EndsWith("Stride.Physics.dll")) //todo hard-coded! needs to go when plug in system is in
                                    {
                                        assemblyProcessorApp.ReferencesToAdd.Add(referencePath.EvaluatedInclude);
                                    }
                                    else if (referencePath.EvaluatedInclude.EndsWith("Stride.Particles.dll")) //todo hard-coded! needs to go when plug in system is in
                                    {
                                        assemblyProcessorApp.ReferencesToAdd.Add(referencePath.EvaluatedInclude);
                                    }
                                    else if (referencePath.EvaluatedInclude.EndsWith("Stride.Native.dll")) //todo hard-coded! needs to go when plug in system is in
                                    {
                                        assemblyProcessorApp.ReferencesToAdd.Add(referencePath.EvaluatedInclude);
                                    }
                                    else if (referencePath.EvaluatedInclude.EndsWith("Stride.UI.dll")) //todo hard-coded! needs to go when plug in system is in
                                    {
                                        assemblyProcessorApp.ReferencesToAdd.Add(referencePath.EvaluatedInclude);
                                    }
                                    else if (referencePath.EvaluatedInclude.EndsWith("Stride.Video.dll")) //todo hard-coded! needs to go when plug in system is in
                                    {
                                        assemblyProcessorApp.ReferencesToAdd.Add(referencePath.EvaluatedInclude);
                                    }
                                }

                                var assemblyResolver = assemblyProcessorApp.CreateAssemblyResolver();

                                // Add dependencies to assembly resolver
                                var recursiveDependencies = stronglyConnected.OutEdges(sourceGroup).SelectDeep(edge => stronglyConnected.OutEdges(edge.Target));
                                foreach (var dependencySourceGroup in recursiveDependencies)
                                {
                                    assemblyResolver.Register(dependencySourceGroup.Target.Assembly, dependencySourceGroup.Target.PE);
                                    assemblyProcessorApp.MemoryReferences.Add(dependencySourceGroup.Target.Assembly);
                                }

                                // Rewind streams
                                peStream.Position  = 0;
                                pdbStream.Position = 0;

                                var assemblyDefinition = AssemblyDefinition.ReadAssembly(peStream,
                                                                                         new ReaderParameters {
                                    AssemblyResolver = assemblyResolver, ReadSymbols = true, SymbolStream = pdbStream
                                });

                                // Run assembly processor
                                bool readWriteSymbols = true;
                                bool modified;
                                assemblyProcessorApp.SerializationAssembly = true;
                                if (!assemblyProcessorApp.Run(ref assemblyDefinition, ref readWriteSymbols, out modified, out var _))
                                {
                                    result.Error("Error running assembly processor");
                                    break;
                                }

                                sourceGroup.Assembly = assemblyDefinition;

                                // Write to file for now, since Cecil does not use the SymbolStream
                                var peFileName  = Path.ChangeExtension(Path.GetTempFileName(), ".dll");
                                var pdbFileName = Path.ChangeExtension(peFileName, ".pdb");
                                assemblyDefinition.Write(peFileName, new WriterParameters {
                                    WriteSymbols = true
                                });

                                sourceGroup.PE  = File.ReadAllBytes(peFileName);
                                sourceGroup.PDB = File.ReadAllBytes(pdbFileName);

                                File.Delete(peFileName);
                                File.Delete(pdbFileName);
                            }
                            else
                            {
                                sourceGroup.PE  = peStream.ToArray();
                                sourceGroup.PDB = pdbStream.ToArray();
                            }
                        }
                }
            }

            // We register unloading/loading only if everything succeeded
            if (!result.HasErrors)
            {
                // 3. Register old assemblies to unload
                foreach (var sourceGroup in previousSortedConnectedGroups)
                {
                    if (groupsToUnload.Contains(sourceGroup))
                    {
                        sourceGroup.Project  = null;
                        sourceGroup.Assembly = null;
                        result.UnloadedProjects.Add(sourceGroup);
                    }
                }

                // 4. Register new assemblies to load
                foreach (var sourceGroup in sortedConnectedGroups.Reverse())
                {
                    // Check if it's either a new group, or one that has been unloaded
                    if (!previousConnectedGroups.Contains(sourceGroup) || groupsToUnload.Contains(sourceGroup))
                    {
                        result.LoadedProjects.Add(sourceGroup);
                    }
                }

                // Set as new state
                previousSortedConnectedGroups = sortedConnectedGroups;
                previousStronglyConnected     = stronglyConnected;
                previousConnectedGroups       = connectedGroups;
            }

            return(result);
        }