Compare commits

..

18 commits

Author SHA1 Message Date
Ell
bbb4b539cc 3.1.2 2023-12-21 21:28:42 +01:00
Ell
01b43d887d return an exit code when failing 2023-12-21 21:27:52 +01:00
Ell
2b2a507050 3.1.1 2023-12-21 21:23:02 +01:00
Ell
280d92edf3 fixed argument oder being incorrect corrupting project files 2023-12-21 21:20:47 +01:00
Ell
5897609dd2 3.1.0 2023-12-21 19:13:37 +01:00
Max Kopjev
18ce5e5863
Fix creating test/nuget.config. (#6) 2023-12-21 19:09:18 +01:00
Ell
760ee53b54 dependency update 2023-12-21 19:01:45 +01:00
Ell
37876915ee made the Test content file make a bit more sense 2023-12-21 18:45:12 +01:00
Ell
2aee01893d update nuspec to .net 8 also 2023-12-21 18:40:47 +01:00
Ell
e69aa3e089 document references 2023-12-21 18:39:15 +01:00
Ell
95dba50148 stop doing this to me rider 2023-12-21 18:34:16 +01:00
Ell
b2affa9f88 updated to .net 8 lts 2023-12-21 18:33:46 +01:00
Ell
ec0eb59871 some more cleanup and path char inconsistency fixes 2023-12-21 18:28:53 +01:00
Ell
1dfd963dc9 cleaned up new features 2023-12-21 18:11:43 +01:00
Ell
10a7cd947c restore test before building 2023-12-21 18:05:04 +01:00
Ell
2058192fbe fixed missing dotnet tools 2023-12-21 17:59:13 +01:00
Ell
379b3b016d build content as well when testing 2023-12-21 17:50:44 +01:00
Max Kopjev
a10689e3bb
Add auto syncing references (#4)
* Add extraction of project file path.

* Add field for reference syncing in config file.

* Add new dependencies.

* Add sync algorithm for references.

* Update version

* Fix ups

* Rename ReferenceHeader

* Fix message

* Check arguments count.

* Fix

* Revert "Update version"

This reverts commit e24d75d18a.

* Change error logic - no return

* Add adding new references.

* Add more true NuGet support.

* Fix braces.

* Fix First call.

---------

Co-authored-by: Ell <me@ellpeck.de>
2023-12-21 17:48:16 +01:00
12 changed files with 212 additions and 77 deletions

View file

@ -1,9 +1,11 @@
steps: steps:
build: build:
image: mcr.microsoft.com/dotnet/sdk:6.0 image: mcr.microsoft.com/dotnet/sdk:8.0-jammy
commands: commands:
- dotnet build Contentless - dotnet build Contentless
test: test:
image: mcr.microsoft.com/dotnet/sdk:6.0 image: mcr.microsoft.com/dotnet/sdk:8.0-jammy
commands: commands:
- dotnet run --project Contentless Test/Content/Content.mgcb - dotnet restore Test
- dotnet run --project Contentless Test/Content/Content.mgcb Test/Test.csproj
- dotnet build Test

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -14,6 +15,8 @@ public class Config {
[JsonProperty("overrides")] [JsonProperty("overrides")]
public Dictionary<string, Override> Overrides = new(); public Dictionary<string, Override> Overrides = new();
[JsonProperty("references")]
public string[] References = Array.Empty<string>();
} }
public class Override { public class Override {

View file

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<RollForward>Major</RollForward> <RollForward>Major</RollForward>
</PropertyGroup> </PropertyGroup>
@ -11,15 +11,21 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MonoGame.Framework.Content.Pipeline" Version="3.8.0.1641"> <PackageReference Include="MonoGame.Framework.Content.Pipeline" Version="3.8.1.303">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1641"> <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1"> <PackageReference Include="Newtonsoft.Json" Version="13.0.1">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Build" Version="17.3.2">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="NuGet.Configuration" Version="6.8.0">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -10,15 +10,15 @@
<repository type="git" url="https://github.com/Ellpeck/Contentless" /> <repository type="git" url="https://github.com/Ellpeck/Contentless" />
<readme>README.md</readme> <readme>README.md</readme>
<icon>Logo.png</icon> <icon>Logo.png</icon>
<version>3.0.7</version> <version>3.1.2</version>
<dependencies> <dependencies>
<group targetFramework="net6.0" /> <group targetFramework="net8.0" />
</dependencies> </dependencies>
</metadata> </metadata>
<files> <files>
<file src="_._" target="lib/net6.0/" /> <file src="_._" target="lib/net8.0/" />
<file src="Contentless.targets" target="build/Contentless.targets" /> <file src="Contentless.targets" target="build/Contentless.targets" />
<file src="bin\Debug\net6.0\**\*" target="tools/" /> <file src="bin\Debug\net8.0\**\*" target="tools/" />
<file src="../README.md" target="" /> <file src="../README.md" target="" />
<file src="../Logo.png" target="" /> <file src="../Logo.png" target="" />
</files> </files>

View file

@ -1,5 +1,5 @@
<Project> <Project>
<Target Name="Contentless" BeforeTargets="BeforeBuild"> <Target Name="Contentless" BeforeTargets="BeforeBuild">
<Exec Command="dotnet $(MSBuildThisFileDirectory)/../tools/Contentless.dll @(MonoGameContentReference)" /> <Exec Command="dotnet $(MSBuildThisFileDirectory)/../tools/Contentless.dll @(MonoGameContentReference) $(MSBuildProjectFullPath)" />
</Target> </Target>
</Project> </Project>

View file

@ -4,23 +4,25 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.Build.Construction;
using Microsoft.Xna.Framework.Content.Pipeline; using Microsoft.Xna.Framework.Content.Pipeline;
using Newtonsoft.Json; using Newtonsoft.Json;
using NuGet.Configuration;
namespace Contentless; namespace Contentless;
public static class Program { public static class Program {
public static void Main(string[] args) { public static int Main(string[] args) {
if (args.Length != 1) { if (args.Length < 1) {
Console.WriteLine("Please specify the location of the content file you want to use"); Console.Error.WriteLine("Please specify the location of the content file you want to use");
return; return 1;
} }
var contentFile = new FileInfo(Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, args[0]))); var contentFile = new FileInfo(Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, args[0])));
if (!contentFile.Exists) { if (!contentFile.Exists || contentFile.Extension != ".mgcb") {
Console.WriteLine($"Unable to find content file {contentFile}"); Console.Error.WriteLine($"Unable to find valid content file at {contentFile}");
return; return 1;
} }
Console.WriteLine($"Using content file {contentFile}"); Console.WriteLine($"Using content file {contentFile}");
@ -35,7 +37,8 @@ public static class Program {
config = JsonConvert.DeserializeObject<Config>(stream.ReadToEnd()); config = JsonConvert.DeserializeObject<Config>(stream.ReadToEnd());
Console.WriteLine($"Using config from {configFile}"); Console.WriteLine($"Using config from {configFile}");
} catch (Exception e) { } catch (Exception e) {
Console.WriteLine($"Error loading config from {configFile}: {e}"); Console.Error.WriteLine($"Error loading config from {configFile}: {e}");
return 1;
} }
} else { } else {
Console.WriteLine("Using default config"); Console.WriteLine("Using default config");
@ -43,31 +46,89 @@ public static class Program {
var excluded = config.ExcludedFiles.Select(Program.MakeFileRegex).ToArray(); var excluded = config.ExcludedFiles.Select(Program.MakeFileRegex).ToArray();
var overrides = Program.GetOverrides(config.Overrides).ToArray(); var overrides = Program.GetOverrides(config.Overrides).ToArray();
// load any references to be able to include custom content types as well string packagesFolder = null;
foreach (var line in content) { var referencesVersions = config.References.ToDictionary(x => x, _ => (string) null, StringComparer.OrdinalIgnoreCase);
if (!line.StartsWith("/reference:")) if (config.References.Length > 0) {
continue; if (args.Length > 1) {
var reference = line.Substring(11); var csprojFullPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, args[1]));
var refPath = Path.GetFullPath(Path.Combine(contentFile.DirectoryName, reference)); if (!File.Exists(csprojFullPath) || Path.GetExtension(csprojFullPath) != ".csproj") {
try { Console.Error.WriteLine($"Unable to find valid project file at {contentFile}");
Assembly.LoadFrom(refPath); return 1;
Console.WriteLine($"Using reference {refPath}"); }
} catch (Exception e) { Program.ExtractVersions(csprojFullPath, referencesVersions);
Console.WriteLine($"Error loading reference {refPath}: {e}"); var settings = Settings.LoadDefaultSettings(Path.GetDirectoryName(csprojFullPath));
packagesFolder = SettingsUtility.GetGlobalPackagesFolder(settings);
} else {
Console.Error.WriteLine("The config file contains references, but no project file was specified. Please specify the location of the content file you want to use for gathering references as the second argument.");
} }
} }
const string referenceHeader = "/reference:";
var changed = false;
var referencesSyncs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
// load any references to be able to include custom content types as well
for (var i = 0; i < content.Count; i++) {
var line = content[i];
if (!line.StartsWith(referenceHeader))
continue;
var reference = line[referenceHeader.Length..];
var libraryName = Path.GetFileName(reference)[..^4];
if (referencesVersions.TryGetValue(libraryName, out var version) && version is not null) {
var fullLibraryPath = Program.CalculateFullPathToLibrary(packagesFolder, libraryName, version);
if (reference != fullLibraryPath) {
Console.WriteLine($"Changing reference from {reference} to {fullLibraryPath}");
reference = fullLibraryPath;
content[i] = referenceHeader + fullLibraryPath;
changed = true;
} else {
if (config.LogSkipped)
Console.WriteLine($"Skipping reference replacement for {fullLibraryPath} which already matched");
}
referencesSyncs.Add(libraryName);
}
var refPath = Path.GetFullPath(Path.Combine(contentFile.DirectoryName, reference));
Program.SafeAssemblyLoad(refPath);
Console.WriteLine($"Using reference {refPath}");
}
// check references not in .mgcb now
var referencesLastIndex = 0;
// find place where I can add new reference
for (var i = 0; i < content.Count; i++) {
var line = content[i];
if (line.StartsWith(referenceHeader)) {
referencesLastIndex = i + 1;
} else if (line.StartsWith("/importer:") || line.StartsWith("/processor:") || line.StartsWith("/build:") || line.Contains("-- Content --")) {
if (referencesLastIndex == 0)
referencesLastIndex = i;
break;
}
}
foreach (var reference in referencesVersions)
if (!referencesSyncs.Contains(reference.Key) && reference.Value is not null) {
try {
var path = Program.CalculateFullPathToLibrary(packagesFolder, reference.Key, reference.Value);
content.Insert(referencesLastIndex++, referenceHeader + path);
changed = true;
Program.SafeAssemblyLoad(path);
Console.WriteLine($"Adding reference {path}");
} catch (Exception e) {
Console.Error.WriteLine($"Error adding reference {reference.Key} {e}");
}
}
// load content importers // load content importers
var (importers, processors) = Program.GetContentData(); var (importers, processors) = Program.GetContentData();
Console.WriteLine($"Found possible importer types {string.Join(", ", importers)}"); Console.WriteLine($"Found possible importer types {string.Join(", ", importers)}");
Console.WriteLine($"Found possible processor types {string.Join(", ", processors)}"); Console.WriteLine($"Found possible processor types {string.Join(", ", processors)}");
var changed = false;
foreach (var file in contentFile.Directory.EnumerateFiles("*", SearchOption.AllDirectories)) { foreach (var file in contentFile.Directory.EnumerateFiles("*", SearchOption.AllDirectories)) {
// is the file the content or config file? // is the file the content or config file?
if (file.Name == contentFile.Name || file.Name == configFile.Name) if (file.Name == contentFile.Name || file.Name == configFile.Name)
continue; continue;
var relative = Program.GetRelativePath(contentFile.DirectoryName, file.FullName).Replace("\\", "/"); var relative = Path.GetRelativePath(contentFile.DirectoryName, file.FullName).Replace('\\', '/');
// is the file in an excluded directory? // is the file in an excluded directory?
if (excluded.Any(e => e.IsMatch(relative))) { if (excluded.Any(e => e.IsMatch(relative))) {
@ -102,7 +163,7 @@ public static class Program {
if (!string.IsNullOrEmpty(over.Override.Importer)) { if (!string.IsNullOrEmpty(over.Override.Importer)) {
importer = importers.Find(i => i.Type.Name == over.Override.Importer); importer = importers.Find(i => i.Type.Name == over.Override.Importer);
if (importer == null) { if (importer == null) {
Console.WriteLine($"Override importer {over.Override.Importer} not found for file {relative}"); Console.Error.WriteLine($"Override importer {over.Override.Importer} not found for file {relative}");
continue; continue;
} }
} }
@ -110,7 +171,7 @@ public static class Program {
if (!string.IsNullOrEmpty(over.Override.Processor)) { if (!string.IsNullOrEmpty(over.Override.Processor)) {
processor = processors.Find(p => p == over.Override.Processor); processor = processors.Find(p => p == over.Override.Processor);
if (processor == null) { if (processor == null) {
Console.WriteLine($"Override processor {over.Override.Processor} not found for file {relative}"); Console.Error.WriteLine($"Override processor {over.Override.Processor} not found for file {relative}");
continue; continue;
} }
} }
@ -123,7 +184,7 @@ public static class Program {
// no importer found :( // no importer found :(
if (importer == null || processor == null) { if (importer == null || processor == null) {
Console.WriteLine($"No importer or processor found for file {relative}"); Console.Error.WriteLine($"No importer or processor found for file {relative}");
continue; continue;
} }
@ -140,6 +201,38 @@ public static class Program {
Console.WriteLine("Wrote changes to content file"); Console.WriteLine("Wrote changes to content file");
} }
Console.Write("Done"); Console.Write("Done");
return 0;
}
private static void SafeAssemblyLoad(string refPath) {
try {
Assembly.LoadFrom(refPath);
} catch (Exception e) {
Console.Error.WriteLine($"Error loading reference {refPath} {e}");
}
}
private static void ExtractVersions(string csprojPath, Dictionary<string, string> referencesVersions) {
Console.WriteLine($"Using project file {csprojPath}");
var projectRootElement = ProjectRootElement.Open(csprojPath);
foreach (var property in projectRootElement.AllChildren.Where(x => x.ElementName == "PackageReference").Select(x => x as ProjectItemElement)) {
var libraryName = property.Include;
if (property.Children.FirstOrDefault(x => x.ElementName == "Version") is not ProjectMetadataElement versionElement)
continue;
var version = versionElement.Value;
if (referencesVersions.ContainsKey(libraryName)) {
referencesVersions[libraryName] = version;
Console.WriteLine($"Found reference {libraryName} {version} in project file");
}
}
foreach (var library in referencesVersions)
if (library.Value is null)
Console.Error.WriteLine($"Unable to find reference {library.Key} in project file");
}
private static string CalculateFullPathToLibrary(string packageFolder, string libraryName, string referencesVersion) {
return Path.Combine(packageFolder, libraryName.ToLowerInvariant(), referencesVersion, "tools", libraryName + ".dll").Replace('\\', '/');
} }
private static (List<ImporterInfo>, List<string>) GetContentData() { private static (List<ImporterInfo>, List<string>) GetContentData() {
@ -156,7 +249,7 @@ public static class Program {
processors.Add(type.Name); processors.Add(type.Name);
} }
} catch (Exception e) { } catch (Exception e) {
Console.WriteLine($"Error gathering types in reference {assembly}: {e}"); Console.Error.WriteLine($"Error gathering types in reference {assembly}: {e}");
} }
} }
return (importers, processors); return (importers, processors);
@ -220,12 +313,6 @@ public static class Program {
Console.WriteLine($"Adding file {relative} with the Copy build action"); Console.WriteLine($"Adding file {relative} with the Copy build action");
} }
private static string GetRelativePath(string relativeTo, string path) {
if (!relativeTo.EndsWith(Path.DirectorySeparatorChar.ToString()))
relativeTo += Path.DirectorySeparatorChar;
return path.Replace(relativeTo, "");
}
private static Regex MakeFileRegex(string s) { private static Regex MakeFileRegex(string s) {
return new Regex(s.Replace(".", "[.]").Replace("*", ".*").Replace("?", ".")); return new Regex(s.Replace(".", "[.]").Replace("*", ".*").Replace("?", "."));
} }

View file

@ -59,7 +59,10 @@ If you want to change the way Contentless works, you can use a configuration fil
"TextureFormat": "Compressed" "TextureFormat": "Compressed"
} }
} }
} },
// A set of content pipeline library references that should optionally be added to the content file, or whose paths should be changed in the content file if they don't match the project's package references
// Default: []
"references": ["MonoGame.Extended.Content.Pipeline"]
} }
``` ```
For an example of a config in use, see the [test config](https://github.com/Ellpeck/Contentless/blob/main/Test/Content/Contentless.json). For an example of a config in use, see the [test config](https://github.com/Ellpeck/Contentless/blob/main/Test/Content/Contentless.json).

View file

@ -0,0 +1,36 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-mgcb": {
"version": "3.8.1.303",
"commands": [
"mgcb"
]
},
"dotnet-mgcb-editor": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor"
]
},
"dotnet-mgcb-editor-linux": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor-linux"
]
},
"dotnet-mgcb-editor-windows": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor-windows"
]
},
"dotnet-mgcb-editor-mac": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor-mac"
]
}
}
}

View file

@ -10,32 +10,31 @@
#-------------------------------- References --------------------------------# #-------------------------------- References --------------------------------#
/reference:..\..\..\.nuget\packages\monogame.extended.content.pipeline\3.8.0\tools\MonoGame.Extended.Content.Pipeline.dll /reference:C:/Users/me/.nuget/packages/monogame.extended.content.pipeline/3.9.0-alpha0093/tools/MonoGame.Extended.Content.Pipeline.dll
#---------------------------------- Content ---------------------------------# #---------------------------------- Content ---------------------------------#
#begin Json/Copy.json #begin Json/Copy.json
/copy:Json/Copy.json /copy:Json/Copy.json
#begin Json/Test.json #begin Json/Test.json
/importer:JsonContentImporter /copy:Json/Test.json
/processor:FontTextureProcessor
/build:Json/Test.json
#begin Locale/Interface.xml #begin Locale/Interface.xml
/importer:XmlImporter /importer:XmlImporter
/processor:FontTextureProcessor /processor:PassThroughProcessor
/build:Locale/Interface.xml /build:Locale/Interface.xml
#begin Textures/Icons.png #begin Textures/Icons.png
/importer:TextureImporter /importer:TextureImporter
/processor:TextureProcessor /processor:TextureProcessor
/processorParam:TextureFormat=Compressed /processorParam:TextureFormat=NoChange
/build:Textures/Icons.png /build:Textures/Icons.png
#begin Textures/Inside.png #begin Textures/Inside.png
/importer:TextureImporter /importer:TextureImporter
/processor:TextureProcessor /processor:TextureProcessor
/processorParam:TextureFormat=Compressed /processorParam:TextureFormat=NoChange
/build:Textures/Inside.png /build:Textures/Inside.png
#begin Tiled/Map.tmx #begin Tiled/Map.tmx
@ -43,14 +42,7 @@
/processor:TiledMapProcessor /processor:TiledMapProcessor
/build:Tiled/Map.tmx /build:Tiled/Map.tmx
#begin Tiled/Tiles.png
/importer:TextureImporter
/processor:TextureProcessor
/processorParam:TextureFormat=Compressed
/build:Tiled/Tiles.png
#begin Tiled/Tileset.tsx #begin Tiled/Tileset.tsx
/importer:TiledMapTilesetImporter /importer:TiledMapTilesetImporter
/processor:TiledMapTilesetProcessor /processor:TiledMapTilesetProcessor
/build:Tiled/Tileset.tsx /build:Tiled/Tileset.tsx

View file

@ -2,27 +2,25 @@
"exclude": [ "exclude": [
"obj/", "obj/",
"bin/", "bin/",
"Ex*.png" "Ex*.png",
"Tiled/*.png"
], ],
"logSkipped": true, "logSkipped": true,
"references": ["MonoGame.Extended.Content.Pipeline"],
"overrides": { "overrides": {
"Copy.*": { "Copy.*": {
"copy": true "copy": true
}, },
".json": { ".json": {
"importer": "JsonContentImporter", "copy": true
"processor": "FontTextureProcessor"
}, },
".ogg": { ".ogg": {
"importer": "OggImporter", "importer": "OggImporter",
"processor": "SongProcessor" "processor": "SongProcessor"
}, },
".xml": {
"processor": "FontTextureProcessor"
},
".png": { ".png": {
"processorParams": { "processorParams": {
"TextureFormat": "Compressed" "TextureFormat": "NoChange"
} }
} }
} }

7
Test/NuGet.Config Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="lithiumtoast" value="https://www.myget.org/F/lithiumtoast/api/v3/index.json" />
</packageSources>
</configuration>

View file

@ -1,15 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<MonoGamePlatform>DesktopGL</MonoGamePlatform>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MonoGame.Extended.Content.Pipeline" Version="3.8.0" /> <PackageReference Include="MonoGame.Extended.Content.Pipeline" Version="3.9.0-alpha0093" />
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="Content\Content.mgcb" />
<MonoGameContentReference Include="Content\Content.mgcb" /> <MonoGameContentReference Include="Content\Content.mgcb" />
</ItemGroup> </ItemGroup>