Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Build Process Automation Friendlier #47

Closed
bklooster opened this issue Jul 9, 2021 · 5 comments · Fixed by #48
Closed

Make Build Process Automation Friendlier #47

bklooster opened this issue Jul 9, 2021 · 5 comments · Fixed by #48

Comments

@bklooster
Copy link

This is more of a request than a bug/issue, but from what I'm seeing in order to automate this process via a build script I need to basically rewrite the entire GdkBuildEditorWindow. I'd love it if the Build method could be static or moved into EditorHelpers so I could more easily call this from my automated build scripts. Something like a PreBuild() and PostBuild() would be helpful. Thanks!

@payzer
Copy link
Member

payzer commented Jul 9, 2021

Thank you for the suggestion! That makes sense. We'll look into doing the change.

@payzer
Copy link
Member

payzer commented Jul 13, 2021

This change will go out in our next release, but if you want it sooner you can pull from the repo. Thank you again for the suggestion!

@MonsterArtur1
Copy link

MonsterArtur1 commented Sep 29, 2021

Alternative solution:
I know its closed and tweaked, but I'm using older version, so I use this Shell script for making GDK build on our Jenkins:

  • just build standard PC x64 build with plugins enabled
  • run this as next build step to generate package
copy %WORKSPACE%\Assets\GDK-Tools\ProjectMetadata\MicrosoftGame.Config D:\OutputDir\Jenkins\ProjectName\StandaloneWindows64_GDK\
copy %WORKSPACE%\Assets\GDK-Tools\ProjectMetadata\*.png D:\OutputDir\Jenkins\ProjectName\StandaloneWindows64_GDK\

rmdir D:\OutputDir\Jenkins\ProjectName\PC_GDK\ /s /q 
mkdir D:\OutputDir\Jenkins\ProjectName\PC_GDK
rmdir D:\OutputDir\Jenkins\ProjectName\StandaloneWindows64_GDK\ProjectName_BackUpThisFolder_ButDontShipItWithYourGame /s /q 
move D:\OutputDir\Jenkins\ProjectName\StandaloneWindows64_GDK D:\OutputDir\Jenkins\ProjectName\PC_GDK\Win32

"C:\Program Files (x86)\Microsoft GDK\bin\makepkg.exe" genmap /f D:\OutputDir\Jenkins\ProjectName\PC_GDK\Win32\layout.xml /d D:\OutputDir\Jenkins\ProjectName\PC_GDK\Win32\

mkdir D:\OutputDir\Jenkins\ProjectName\PC_GDK\GDK

"C:\Program Files (x86)\Microsoft GDK\bin\makepkg.exe" pack /f D:\OutputDir\Jenkins\ProjectName\PC_GDK\Win32\layout.xml /d D:\OutputDir\Jenkins\ProjectName\PC_GDK\Win32\ /pd D:\OutputDir\Jenkins\ProjectName\PC_GDK\GDK /pc /CorrelationId Unity /l

maybe this could help someone

@payzer
Copy link
Member

payzer commented Sep 29, 2021

Thank you so much for posting that script! I'm sure many others will absolutely benefit from it.

@Warhammer4000
Copy link

I've actually gone ahead and wrote prebuild & postbuild script for Automation. This is basically code copied out from existing GDKBuild.cs

I'm sure this can be more cleaner with having scriptable object to store some of the configuration

PreBuild.cs

using Microsoft.GameCore.Utilities;
using System.Collections.Generic;
using System.Xml.Linq;
using System;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;


public class PreBuild : IPreprocessBuildWithReport
{

    public static string contentIdOverride;
    public static string originalSquare150x150LogoPath;
    public static string originalSquare44x44LogoPath;
    public static string originalSquare480x480LogoPath;
    public static string originalSplashScreenImagePath;
    public static string originalStoreLogoPath;


    public int callbackOrder => 0;
    public void OnPreprocessBuild(BuildReport report)
    {
        Setup(Path.GetDirectoryName(report.summary.outputPath));
    }


    private void Setup(string pcStandaloneBuildPath)
    {

        PlayerSettings.forceSingleInstance = true;

        GdkUtilities.PullGdkDlls();

        CopyManifestFiles(pcStandaloneBuildPath);

   
    }

    private void CopyManifestFiles(string buildWin32OutputFolderPath)
    {
        string square150x150LogoPath = string.Empty;
        string square44x44LogoPath = string.Empty;
        string square480x480LogoPath = string.Empty;
        string splashScreenImagePath = string.Empty;
        string storeLogoPath = string.Empty;

        string gameConfigFilePath = GdkUtilities.GameConfigPath;


        XDocument gameConfigXmlDoc = XDocument.Load(gameConfigFilePath);
        try
        {
            string imagesPath = gameConfigFilePath.Replace("MicrosoftGame.Config", string.Empty);
            XElement executableEl = (from executable in gameConfigXmlDoc.Descendants("Executable")
                                     select executable).First();
            executableEl.SetAttributeValue("Name", PlayerSettings.productName + ".exe");

            // Find the images
            XElement shellVisualsEl = (from shellVisual in gameConfigXmlDoc.Descendants("ShellVisuals")
                                       select shellVisual).First();

            XAttribute square150x150LogoAttribute = shellVisualsEl.Attribute("Square150x150Logo");
            originalSquare150x150LogoPath = square150x150LogoAttribute.Value;
            square150x150LogoPath = (imagesPath + originalSquare150x150LogoPath).Replace("/", "\\");

            XAttribute square44x44LogoAttribute = shellVisualsEl.Attribute("Square44x44Logo");
            originalSquare44x44LogoPath = square44x44LogoAttribute.Value;
            square44x44LogoPath = (imagesPath + originalSquare44x44LogoPath).Replace("/", "\\");

            XAttribute square480x480LogoAttribute = shellVisualsEl.Attribute("Square480x480Logo");
            originalSquare480x480LogoPath = square480x480LogoAttribute.Value;
            square480x480LogoPath = (imagesPath + originalSquare480x480LogoPath).Replace("/", "\\");

            XAttribute splashScreenImageAttribute = shellVisualsEl.Attribute("SplashScreenImage");
            originalSplashScreenImagePath = splashScreenImageAttribute.Value;
            splashScreenImagePath = (imagesPath + originalSplashScreenImagePath).Replace("/", "\\");

            XAttribute storeLogoAttribute = shellVisualsEl.Attribute("StoreLogo");
            originalStoreLogoPath = storeLogoAttribute.Value;
            storeLogoPath = (imagesPath + originalStoreLogoPath).Replace("/", "\\");

            // Check for a Content ID override
            XElement contentIdOverrideEl = (from contentIdOverride in gameConfigXmlDoc.Descendants("ContentIdOverride")
                                            select contentIdOverride).First();
            contentIdOverride = contentIdOverrideEl.Value;

            gameConfigXmlDoc.Save(gameConfigFilePath);
        }
        catch (Exception)
        {
            Debug.LogError("Invalid or corrupt MicrosoftGame.Config.");
            return;
        }

        List<string> storeAssetsToCopy = new List<string>
        {
            gameConfigFilePath,
            square150x150LogoPath,
            square44x44LogoPath,
            square480x480LogoPath,
            splashScreenImagePath,
            storeLogoPath
        };

        foreach (string storeAssetToCopy in storeAssetsToCopy)
        {
            string fileName = Path.GetFileName(storeAssetToCopy);
            string fullSourcePath = storeAssetToCopy;
            string destinationPath = buildWin32OutputFolderPath + "\\" + fileName;
            File.Copy(fullSourcePath, destinationPath, true);
        }

    }

}

PostBuild.cs

using Microsoft.GameCore.Tools;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using Debug = UnityEngine.Debug;


public class PostBuild : IPostprocessBuildWithReport
{

    private string buildWin32OutputFolderPath;
    private string buildMsixvcOutputFolderPath;

    private const string MsixvcOutputDirectory = "MSIXVC";

    public int callbackOrder => 0;

    public void OnPostprocessBuild(BuildReport report)
    {

        string standaloneBuildPath = Path.GetDirectoryName(report.summary.outputPath);

        string gdkBuildOutputPath = Path.Combine(Directory.GetParent(standaloneBuildPath).FullName, MsixvcOutputDirectory).Replace('/','\\');

        Debug.Log($"GDK Build Path:{gdkBuildOutputPath}");

        Setup(standaloneBuildPath, gdkBuildOutputPath);
        OpenBuildOutputFolder();
    }

    private  bool Setup(string pcStandaloneBuildPath, string gdkBuildOutputFolderPath, bool createPackageForStoreUpload=true)
    {
        try
        {
            if (!Directory.Exists(gdkBuildOutputFolderPath))
            {
                Directory.CreateDirectory(gdkBuildOutputFolderPath);
            }
        }
        catch (Exception)
        {
            Debug.LogError("Could not find or create GDK Build output directory: " + gdkBuildOutputFolderPath);
        }

        buildWin32OutputFolderPath = pcStandaloneBuildPath;
        buildMsixvcOutputFolderPath = gdkBuildOutputFolderPath;

        bool succeeded = PostWin32BuildCleanup();
        if (succeeded)
        {
            succeeded = CreateAndConfigureLayoutFile();
        }
        if (succeeded)
        {
            succeeded = MakePackage(createPackageForStoreUpload);
        }

        return succeeded;
    }


    private  bool PostWin32BuildCleanup()
    {
        bool succeeded = true;

        string gameConfigFilePath = Path.Combine(buildWin32OutputFolderPath, "MicrosoftGame.Config");
        string cleanedGameConfigFilePath = gameConfigFilePath.Replace("/", "\\");

        XDocument gameConfigXmlDoc = XDocument.Load(cleanedGameConfigFilePath);
        XElement shellVisualsEl = (from shellVisual in gameConfigXmlDoc.Descendants("ShellVisuals")
            select shellVisual).First();

        // We need to rewrite the manifest to point at where the images will be placed
        // in the build directory.
        shellVisualsEl.SetAttributeValue("Square150x150Logo", Path.GetFileName(PreBuild.originalSquare150x150LogoPath));
        shellVisualsEl.SetAttributeValue("Square44x44Logo", Path.GetFileName(PreBuild.originalSquare44x44LogoPath));
        shellVisualsEl.SetAttributeValue("Square480x480Logo", Path.GetFileName(PreBuild.originalSquare480x480LogoPath));
        shellVisualsEl.SetAttributeValue("SplashScreenImage", Path.GetFileName(PreBuild.originalSplashScreenImagePath));
        shellVisualsEl.SetAttributeValue("StoreLogo", Path.GetFileName(PreBuild.originalStoreLogoPath));

        gameConfigXmlDoc.Save(cleanedGameConfigFilePath);

        return succeeded;
    }


    private bool CreateAndConfigureLayoutFile()
    {
        string arguments = string.Format("/c makepkg.exe genmap /f \"{0}\\layout.xml\" /d \"{0}\"", buildWin32OutputFolderPath);

        if (!GdkEditorHelpers.StartCmdProcess(arguments))
        {
            return false;
        }

        string layoutPath = buildWin32OutputFolderPath + @"\layout.xml";

        var doc = XDocument.Load(layoutPath);
        var ignoredNodes = (from node in doc.Descendants()
            where node.Name == "FileGroup" && node.Attribute("SourcePath").Value.Contains("Package_BackUpThisFolder_ButDontShipItWithYourGame")
            select node).ToArray();

        for (int i = 0; i < ignoredNodes.Length; ++i)
        {
            ignoredNodes[i].Remove();
        }

        doc.Save(layoutPath);

        return true;
    }

    private bool MakePackage(bool createForStoreUpload=true)
    {
        string makePkgExtraArgs = string.Empty;
        if (!string.IsNullOrEmpty(PreBuild.contentIdOverride))
        {
            makePkgExtraArgs = "/contentid " + PreBuild.contentIdOverride;
        }

        string arguments = string.Format("/c makepkg.exe pack /f \"{0}\\layout.xml\" /d \"{0}\" /pd \"{1}\" /pc /CorrelationId Unity " + makePkgExtraArgs, buildWin32OutputFolderPath, buildMsixvcOutputFolderPath);
        if (createForStoreUpload)
        {
            arguments += " /l";
        }

        return GdkEditorHelpers.StartCmdProcess(arguments);
    }


    private  bool OpenBuildOutputFolder()
    {
        bool succeeded = true;
        Process.Start("explorer.exe", buildMsixvcOutputFolderPath);
        return succeeded;
    }


}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants