mirror of
https://github.com/godotengine/godot.git
synced 2024-11-27 09:16:35 +08:00
C#: Make editor create NuGet fallback folder for Godot packages
Main benefits: - Projects can be built offline. Previously you needed internet access the first time building to download the packages. - Changes to packages like Godot.NET.Sdk can be easily tested before publishing. This was already possible but required too many manual steps. - First time builds are a bit faster, as the Sdk package doesn't need to be downloaded. In practice, the package is very small so it makes little difference. Bumped Godot.NET.Sdk to 4.0.0-dev3 in order to enable the recent changes regarding '.mono/' -> '.godot/mono/'.
This commit is contained in:
parent
d5073c6b4c
commit
64b5ee7010
@ -40,6 +40,11 @@ if env_mono["tools"] and env_mono["mono_glue"] and env_mono["build_cil"]:
|
||||
|
||||
godot_tools_build.build(env_mono, api_sln_cmd)
|
||||
|
||||
# Build Godot.NET.Sdk
|
||||
import build_scripts.godot_net_sdk_build as godot_net_sdk_build
|
||||
|
||||
godot_net_sdk_build.build(env_mono)
|
||||
|
||||
# Add sources
|
||||
|
||||
env_mono.add_source_files(env.modules_sources, "*.cpp")
|
||||
|
45
modules/mono/build_scripts/godot_net_sdk_build.py
Normal file
45
modules/mono/build_scripts/godot_net_sdk_build.py
Normal file
@ -0,0 +1,45 @@
|
||||
# Build Godot.NET.Sdk solution
|
||||
|
||||
import os
|
||||
|
||||
from SCons.Script import Dir
|
||||
|
||||
|
||||
def build_godot_net_sdk(source, target, env):
|
||||
# source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
|
||||
|
||||
module_dir = env["module_dir"]
|
||||
|
||||
solution_path = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln")
|
||||
build_config = "Release"
|
||||
|
||||
from .solution_builder import build_solution
|
||||
|
||||
extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
|
||||
|
||||
build_solution(env, solution_path, build_config, extra_msbuild_args)
|
||||
# No need to copy targets. The Godot.NET.Sdk csproj takes care of copying them.
|
||||
|
||||
|
||||
def build(env_mono):
|
||||
assert env_mono["tools"]
|
||||
|
||||
output_dir = Dir("#bin").abspath
|
||||
editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools")
|
||||
nupkgs_dir = os.path.join(editor_tools_dir, "nupkgs")
|
||||
|
||||
module_dir = os.getcwd()
|
||||
|
||||
package_version_file = os.path.join(
|
||||
module_dir, "editor", "Godot.NET.Sdk", "Godot.NET.Sdk", "Godot.NET.Sdk_PackageVersion.txt"
|
||||
)
|
||||
|
||||
with open(package_version_file, mode="r") as f:
|
||||
version = f.read().strip()
|
||||
|
||||
target_filenames = ["Godot.NET.Sdk.%s.nupkg" % version]
|
||||
|
||||
targets = [os.path.join(nupkgs_dir, filename) for filename in target_filenames]
|
||||
|
||||
cmd = env_mono.CommandNoCache(targets, [], build_godot_net_sdk, module_dir=module_dir)
|
||||
env_mono.AlwaysBuild(cmd)
|
@ -1,13 +1,13 @@
|
||||
<Project Sdk="Microsoft.Build.NoTargets/2.0.1">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
|
||||
<Description>MSBuild .NET Sdk for Godot projects.</Description>
|
||||
<Authors>Godot Engine contributors</Authors>
|
||||
|
||||
<PackageId>Godot.NET.Sdk</PackageId>
|
||||
<Version>4.0.0</Version>
|
||||
<PackageVersion>4.0.0-dev2</PackageVersion>
|
||||
<PackageProjectUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</PackageProjectUrl>
|
||||
<PackageType>MSBuildSdk</PackageType>
|
||||
<PackageTags>MSBuildSdk</PackageTags>
|
||||
@ -19,7 +19,13 @@
|
||||
<GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);SetNuSpecProperties</GenerateNuspecDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') ">
|
||||
<Target Name="ReadGodotNETSdkVersion" BeforeTargets="BeforeBuild;BeforeRebuild;CoreCompile">
|
||||
<PropertyGroup>
|
||||
<PackageVersion>$([System.IO.File]::ReadAllText('$(ProjectDir)Godot.NET.Sdk_PackageVersion.txt').Trim())</PackageVersion>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') " DependsOnTargets="ReadGodotNETSdkVersion">
|
||||
<PropertyGroup>
|
||||
<NuspecProperties>
|
||||
id=$(PackageId);
|
||||
@ -32,4 +38,13 @@
|
||||
</NuspecProperties>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack">
|
||||
<PropertyGroup>
|
||||
<GodotSourceRootPath>$(SolutionDir)\..\..\..\..\</GodotSourceRootPath>
|
||||
<GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir>
|
||||
</PropertyGroup>
|
||||
<Copy SourceFiles="$(OutputPath)$(PackageId).$(PackageVersion).nupkg"
|
||||
DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
@ -0,0 +1 @@
|
||||
4.0.0-dev3
|
@ -10,6 +10,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
|
||||
<ProjectReference Include="..\GodotTools.Shared\GodotTools.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
<!--
|
||||
The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
|
||||
|
@ -3,14 +3,13 @@ using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.Build.Construction;
|
||||
using Microsoft.Build.Evaluation;
|
||||
using GodotTools.Shared;
|
||||
|
||||
namespace GodotTools.ProjectEditor
|
||||
{
|
||||
public static class ProjectGenerator
|
||||
{
|
||||
public const string GodotSdkVersionToUse = "4.0.0-dev2";
|
||||
|
||||
public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GodotSdkVersionToUse}";
|
||||
public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GeneratedGodotNupkgsVersions.GodotNETSdk}";
|
||||
|
||||
public static ProjectRootElement GenGameProject(string name)
|
||||
{
|
||||
|
@ -0,0 +1,36 @@
|
||||
<Project>
|
||||
<!-- Generate C# file with the version of all the nupkgs bundled with Godot -->
|
||||
|
||||
<Target Name="SetPropertiesForGenerateGodotNupkgsVersions">
|
||||
<PropertyGroup>
|
||||
<GodotNETSdkPackageVersionFile>$(SolutionDir)..\Godot.NET.Sdk\Godot.NET.Sdk\Godot.NET.Sdk_PackageVersion.txt</GodotNETSdkPackageVersionFile>
|
||||
<GeneratedGodotNupkgsVersionsFile>$(IntermediateOutputPath)GodotNupkgsVersions.g.cs</GeneratedGodotNupkgsVersionsFile>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="GenerateGodotNupkgsVersionsFile"
|
||||
DependsOnTargets="PrepareForBuild;_GenerateGodotNupkgsVersionsFile"
|
||||
BeforeTargets="BeforeCompile;CoreCompile">
|
||||
<ItemGroup>
|
||||
<Compile Include="$(GeneratedGodotNupkgsVersionsFile)" />
|
||||
<FileWrites Include="$(GeneratedGodotNupkgsVersionsFile)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<Target Name="_GenerateGodotNupkgsVersionsFile"
|
||||
DependsOnTargets="SetPropertiesForGenerateGodotNupkgsVersions"
|
||||
Inputs="$(MSBuildProjectFile);@(GodotNETSdkPackageVersionFile)"
|
||||
Outputs="$(GeneratedGodotNupkgsVersionsFile)">
|
||||
<PropertyGroup>
|
||||
<GenerateGodotNupkgsVersionsCode><![CDATA[
|
||||
namespace $(RootNamespace) {
|
||||
public class GeneratedGodotNupkgsVersions {
|
||||
public const string GodotNETSdk = "$([System.IO.File]::ReadAllText('$(GodotNETSdkPackageVersionFile)').Trim())"%3b
|
||||
}
|
||||
}
|
||||
]]></GenerateGodotNupkgsVersionsCode>
|
||||
</PropertyGroup>
|
||||
<WriteLinesToFile Lines="$(GenerateGodotNupkgsVersionsCode)"
|
||||
File="$(GeneratedGodotNupkgsVersionsFile)"
|
||||
Overwrite="True" WriteOnlyWhenDifferent="True" />
|
||||
</Target>
|
||||
</Project>
|
@ -0,0 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<Import Project="GenerateGodotNupkgsVersions.targets" />
|
||||
</Project>
|
@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.IdeMessaging", "
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio", "GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj", "{EAFFF236-FA96-4A4D-BD23-0E51EF988277}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.Shared", "GodotTools.Shared\GodotTools.Shared.csproj", "{2758FFAF-8237-4CF2-B569-66BF8B3587BB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -43,5 +45,9 @@ Global
|
||||
{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
@ -7,7 +7,6 @@ using JetBrains.Annotations;
|
||||
using static GodotTools.Internals.Globals;
|
||||
using File = GodotTools.Utils.File;
|
||||
using OS = GodotTools.Utils.OS;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace GodotTools.Build
|
||||
{
|
||||
@ -209,6 +208,16 @@ namespace GodotTools.Build
|
||||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
return true; // No solution to build
|
||||
|
||||
try
|
||||
{
|
||||
// Make sure our packages are added to the fallback folder
|
||||
NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Godot.GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
|
||||
}
|
||||
|
||||
GenerateEditorScriptMetadata();
|
||||
|
||||
if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
|
||||
|
@ -33,6 +33,16 @@ namespace GodotTools.Build
|
||||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
return; // No solution to build
|
||||
|
||||
try
|
||||
{
|
||||
// Make sure our packages are added to the fallback folder
|
||||
NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
|
||||
}
|
||||
|
||||
BuildManager.GenerateEditorScriptMetadata();
|
||||
|
||||
if (!BuildManager.BuildProjectBlocking("Debug"))
|
||||
@ -54,6 +64,16 @@ namespace GodotTools.Build
|
||||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
return; // No solution to build
|
||||
|
||||
try
|
||||
{
|
||||
// Make sure our packages are added to the fallback folder
|
||||
NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
|
||||
}
|
||||
|
||||
BuildManager.GenerateEditorScriptMetadata();
|
||||
|
||||
if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Rebuild"}))
|
||||
|
296
modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
Normal file
296
modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
Normal file
@ -0,0 +1,296 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Godot;
|
||||
using GodotTools.Internals;
|
||||
using GodotTools.Shared;
|
||||
using Directory = GodotTools.Utils.Directory;
|
||||
using Environment = System.Environment;
|
||||
using File = GodotTools.Utils.File;
|
||||
|
||||
namespace GodotTools.Build
|
||||
{
|
||||
public static class NuGetUtils
|
||||
{
|
||||
public const string GodotFallbackFolderName = "Godot Offline Packages";
|
||||
|
||||
public static string GodotFallbackFolderPath
|
||||
=> Path.Combine(GodotSharpDirs.MonoUserDir, "GodotNuGetFallbackFolder");
|
||||
|
||||
private static void AddFallbackFolderToNuGetConfig(string nuGetConfigPath, string name, string path)
|
||||
{
|
||||
var xmlDoc = new XmlDocument();
|
||||
xmlDoc.Load(nuGetConfigPath);
|
||||
|
||||
const string nuGetConfigRootName = "configuration";
|
||||
|
||||
var rootNode = xmlDoc.DocumentElement;
|
||||
|
||||
if (rootNode == null)
|
||||
{
|
||||
// No root node, create it
|
||||
rootNode = xmlDoc.CreateElement(nuGetConfigRootName);
|
||||
xmlDoc.AppendChild(rootNode);
|
||||
|
||||
// Since this can be considered pretty much a new NuGet.Config, add the default nuget.org source as well
|
||||
XmlElement nugetOrgSourceEntry = xmlDoc.CreateElement("add");
|
||||
nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = "nuget.org";
|
||||
nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = "https://api.nuget.org/v3/index.json";
|
||||
nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("protocolVersion")).Value = "3";
|
||||
rootNode.AppendChild(xmlDoc.CreateElement("packageSources")).AppendChild(nugetOrgSourceEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check that the root node is the expected one
|
||||
if (rootNode.Name != nuGetConfigRootName)
|
||||
throw new Exception("Invalid root Xml node for NuGet.Config. " +
|
||||
$"Expected '{nuGetConfigRootName}' got '{rootNode.Name}'.");
|
||||
}
|
||||
|
||||
var fallbackFoldersNode = rootNode["fallbackPackageFolders"] ??
|
||||
rootNode.AppendChild(xmlDoc.CreateElement("fallbackPackageFolders"));
|
||||
|
||||
// Check if it already has our fallback package folder
|
||||
for (var xmlNode = fallbackFoldersNode.FirstChild; xmlNode != null; xmlNode = xmlNode.NextSibling)
|
||||
{
|
||||
if (xmlNode.NodeType != XmlNodeType.Element)
|
||||
continue;
|
||||
|
||||
var xmlElement = (XmlElement)xmlNode;
|
||||
if (xmlElement.Name == "add" &&
|
||||
xmlElement.Attributes["key"]?.Value == name &&
|
||||
xmlElement.Attributes["value"]?.Value == path)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
XmlElement newEntry = xmlDoc.CreateElement("add");
|
||||
newEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = name;
|
||||
newEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = path;
|
||||
|
||||
fallbackFoldersNode.AppendChild(newEntry);
|
||||
|
||||
xmlDoc.Save(nuGetConfigPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all the paths where the user NuGet.Config files can be found.
|
||||
/// Does not determine whether the returned files exist or not.
|
||||
/// </summary>
|
||||
private static string[] GetAllUserNuGetConfigFilePaths()
|
||||
{
|
||||
// Where to find 'NuGet/NuGet.Config':
|
||||
//
|
||||
// - Mono/.NETFramework (standalone NuGet):
|
||||
// Uses Environment.SpecialFolder.ApplicationData
|
||||
// - Windows: '%APPDATA%'
|
||||
// - Linux/macOS: '$HOME/.config'
|
||||
// - CoreCLR (dotnet CLI NuGet):
|
||||
// - Windows: '%APPDATA%'
|
||||
// - Linux/macOS: '$DOTNET_CLI_HOME/.nuget' otherwise '$HOME/.nuget'
|
||||
|
||||
string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
|
||||
if (Utils.OS.IsWindows)
|
||||
{
|
||||
// %APPDATA% for both
|
||||
return new[] {Path.Combine(applicationData, "NuGet", "NuGet.Config")};
|
||||
}
|
||||
|
||||
var paths = new string[2];
|
||||
|
||||
// CoreCLR (dotnet CLI NuGet)
|
||||
|
||||
string dotnetCliHome = Environment.GetEnvironmentVariable("DOTNET_CLI_HOME");
|
||||
if (!string.IsNullOrEmpty(dotnetCliHome))
|
||||
{
|
||||
paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "NuGet.Config");
|
||||
}
|
||||
else
|
||||
{
|
||||
string home = Environment.GetEnvironmentVariable("HOME");
|
||||
if (string.IsNullOrEmpty(home))
|
||||
throw new InvalidOperationException("Required environment variable 'HOME' is not set.");
|
||||
paths[0] = Path.Combine(home, ".nuget", "NuGet", "NuGet.Config");
|
||||
}
|
||||
|
||||
// Mono/.NETFramework (standalone NuGet)
|
||||
|
||||
// ApplicationData is $HOME/.config on Linux/macOS
|
||||
paths[1] = Path.Combine(applicationData, "NuGet", "NuGet.Config");
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
// nupkg extraction
|
||||
//
|
||||
// Exclude: (NuGet.Client -> NuGet.Packaging.PackageHelper.ExcludePaths)
|
||||
// package/
|
||||
// _rels/
|
||||
// [Content_Types].xml
|
||||
//
|
||||
// Don't ignore files that begin with a dot (.)
|
||||
//
|
||||
// The nuspec is not lower case inside the nupkg but must be made lower case when extracted.
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified fallback folder to the user NuGet.Config files,
|
||||
/// for both standalone NuGet (Mono/.NETFramework) and dotnet CLI NuGet.
|
||||
/// </summary>
|
||||
public static void AddFallbackFolderToUserNuGetConfigs(string name, string path)
|
||||
{
|
||||
foreach (string nuGetConfigPath in GetAllUserNuGetConfigFilePaths())
|
||||
{
|
||||
if (!System.IO.File.Exists(nuGetConfigPath))
|
||||
{
|
||||
// It doesn't exist, so we create a default one
|
||||
const string defaultConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key=""nuget.org"" value=""https://api.nuget.org/v3/index.json"" protocolVersion=""3"" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
";
|
||||
System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
|
||||
}
|
||||
|
||||
AddFallbackFolderToNuGetConfig(nuGetConfigPath, name, path);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddPackageToFallbackFolder(string fallbackFolder,
|
||||
string nupkgPath, string packageId, string packageVersion)
|
||||
{
|
||||
// dotnet CLI provides no command for this, but we can do it manually.
|
||||
//
|
||||
// - The expected structure is as follows:
|
||||
// fallback_folder/
|
||||
// <package.name>/<version>/
|
||||
// <package.name>.<version>.nupkg
|
||||
// <package.name>.<version>.nupkg.sha512
|
||||
// <package.name>.nuspec
|
||||
// ... extracted nupkg files (check code for excluded files) ...
|
||||
//
|
||||
// - <package.name> and <version> must be in lower case.
|
||||
// - The sha512 of the nupkg is base64 encoded.
|
||||
// - We can get the nuspec from the nupkg which is a Zip file.
|
||||
|
||||
string packageIdLower = packageId.ToLower();
|
||||
string packageVersionLower = packageVersion.ToLower();
|
||||
|
||||
string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower);
|
||||
string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg");
|
||||
string nupkgSha512DestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg.sha512");
|
||||
|
||||
if (File.Exists(nupkgDestPath) && File.Exists(nupkgSha512DestPath))
|
||||
return; // Already added (for speed we don't check if every file is properly extracted)
|
||||
|
||||
Directory.CreateDirectory(destDir);
|
||||
|
||||
// Generate .nupkg.sha512 file
|
||||
|
||||
using (var alg = SHA512.Create())
|
||||
{
|
||||
alg.ComputeHash(File.ReadAllBytes(nupkgPath));
|
||||
string base64Hash = Convert.ToBase64String(alg.Hash);
|
||||
File.WriteAllText(nupkgSha512DestPath, base64Hash);
|
||||
}
|
||||
|
||||
// Extract nupkg
|
||||
ExtractNupkg(destDir, nupkgPath, packageId, packageVersion);
|
||||
|
||||
// Copy .nupkg
|
||||
File.Copy(nupkgPath, nupkgDestPath);
|
||||
}
|
||||
|
||||
private static readonly string[] NupkgExcludePaths =
|
||||
{
|
||||
"_rels/",
|
||||
"package/",
|
||||
"[Content_Types].xml"
|
||||
};
|
||||
|
||||
private static void ExtractNupkg(string destDir, string nupkgPath, string packageId, string packageVersion)
|
||||
{
|
||||
// NOTE: Must use SimplifyGodotPath to make sure we don't extract files outside the destination directory.
|
||||
|
||||
using (var archive = ZipFile.OpenRead(nupkgPath))
|
||||
{
|
||||
// Extract .nuspec manually as it needs to be in lower case
|
||||
|
||||
var nuspecEntry = archive.GetEntry(packageId + ".nuspec");
|
||||
|
||||
if (nuspecEntry == null)
|
||||
throw new InvalidOperationException($"Failed to extract package {packageId}.{packageVersion}. Could not find the nuspec file.");
|
||||
|
||||
nuspecEntry.ExtractToFile(Path.Combine(destDir, nuspecEntry.Name.ToLower().SimplifyGodotPath()));
|
||||
|
||||
// Extract the other package files
|
||||
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
// NOTE: SimplifyGodotPath() removes trailing slash and backslash,
|
||||
// so we can't use the result to check if the entry is a directory.
|
||||
|
||||
string entryFullName = entry.FullName.Replace('\\', '/');
|
||||
|
||||
// Check if the file must be ignored
|
||||
if ( // Excluded files.
|
||||
NupkgExcludePaths.Any(e => entryFullName.StartsWith(e, StringComparison.OrdinalIgnoreCase)) ||
|
||||
// Nupkg hash files and nupkg metadata files on all directory.
|
||||
entryFullName.EndsWith(".nupkg.sha512", StringComparison.OrdinalIgnoreCase) ||
|
||||
entryFullName.EndsWith(".nupkg.metadata", StringComparison.OrdinalIgnoreCase) ||
|
||||
// Nuspec at root level. We already extracted it previously but in lower case.
|
||||
entryFullName.IndexOf('/') == -1 && entryFullName.EndsWith(".nuspec"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string entryFullNameSimplified = entryFullName.SimplifyGodotPath();
|
||||
string destFilePath = Path.Combine(destDir, entryFullNameSimplified);
|
||||
bool isDir = entryFullName.EndsWith("/");
|
||||
|
||||
if (isDir)
|
||||
{
|
||||
Directory.CreateDirectory(destFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(destFilePath));
|
||||
entry.ExtractToFile(destFilePath, overwrite: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies and extracts all the Godot bundled packages to the Godot NuGet fallback folder.
|
||||
/// Does nothing if the packages were already copied.
|
||||
/// </summary>
|
||||
public static void AddBundledPackagesToFallbackFolder(string fallbackFolder)
|
||||
{
|
||||
GD.Print("Copying Godot Offline Packages...");
|
||||
|
||||
string nupkgsLocation = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "nupkgs");
|
||||
|
||||
void AddPackage(string packageId, string packageVersion)
|
||||
{
|
||||
string nupkgPath = Path.Combine(nupkgsLocation, $"{packageId}.{packageVersion}.nupkg");
|
||||
AddPackageToFallbackFolder(fallbackFolder, nupkgPath, packageId, packageVersion);
|
||||
}
|
||||
|
||||
foreach (var (packageId, packageVersion) in PackagesToAdd)
|
||||
AddPackage(packageId, packageVersion);
|
||||
}
|
||||
|
||||
private static readonly (string packageId, string packageVersion)[] PackagesToAdd =
|
||||
{
|
||||
("Godot.NET.Sdk", GeneratedGodotNupkgsVersions.GodotNETSdk)
|
||||
};
|
||||
}
|
||||
}
|
@ -147,6 +147,21 @@ namespace GodotTools
|
||||
case MenuOptions.AboutCSharp:
|
||||
_ShowAboutDialog();
|
||||
break;
|
||||
case MenuOptions.SetupGodotNugetFallbackFolder:
|
||||
{
|
||||
try
|
||||
{
|
||||
string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
|
||||
NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, fallbackFolder);
|
||||
NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ShowErrorDialog("Failed to setup Godot NuGet Offline Packages: " + e.Message);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid menu option");
|
||||
}
|
||||
@ -183,6 +198,7 @@ namespace GodotTools
|
||||
{
|
||||
CreateSln,
|
||||
AboutCSharp,
|
||||
SetupGodotNugetFallbackFolder,
|
||||
}
|
||||
|
||||
public void ShowErrorDialog(string message, string title = "Error")
|
||||
@ -426,6 +442,7 @@ namespace GodotTools
|
||||
// TODO: Remove or edit this info dialog once Mono support is no longer in alpha
|
||||
{
|
||||
menuPopup.AddItem("About C# support".TTR(), (int)MenuOptions.AboutCSharp);
|
||||
menuPopup.AddItem("Setup Godot NuGet Offline Packages".TTR(), (int)MenuOptions.SetupGodotNugetFallbackFolder);
|
||||
aboutDialog = new AcceptDialog();
|
||||
editorBaseControl.AddChild(aboutDialog);
|
||||
aboutDialog.Title = "Important: C# support is not feature-complete";
|
||||
@ -535,6 +552,16 @@ namespace GodotTools
|
||||
exportPlugin.RegisterExportSettings();
|
||||
exportPluginWeak = WeakRef(exportPlugin);
|
||||
|
||||
try
|
||||
{
|
||||
// At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
|
||||
NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, NuGetUtils.GodotFallbackFolderPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GD.PushError("Failed to add Godot NuGet Offline Packages to NuGet.Config: " + e.Message);
|
||||
}
|
||||
|
||||
BuildManager.Initialize();
|
||||
RiderPathManager.Initialize();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user