hadashiA / VContainer

The extra fast, minimum code size, GC-free DI (Dependency Injection) library running on Unity Game Engine.
https://vcontainer.hadashikick.jp
MIT License
1.89k stars 165 forks source link

In Unity 2023.1 all unused constructors are stripped when ManagedStrippingLevel is set to High #530

Closed noncasted closed 3 months ago

noncasted commented 1 year ago

I recently updated my project to Unity 2023.1 and faced a problem, when VContainer works in editor but in build I receive an error message: "Type does not found injectable constructor, type" cause my ManagedStrippingLevel was set to High.

I temporarily solved this issue by passing all my asmdefs to link.xml, so they are ignored by stripping. It was done by this script, if some one is facing the same problem.

    public class LinkerGenerator : IPreprocessBuildWithReport
    {
        private const string _sourcesFolder = "/Features/";

        public int callbackOrder { get; }

        public void OnPreprocessBuild(BuildReport report)
        {
            Generate();
        }

        [MenuItem("Tools/Generate link.xml")]
        public static void Generate()
        {
            var assetsDir = Application.dataPath;

            var linkXmlFilePath = Path.Combine(assetsDir, Application.dataPath + "/Settings/", "link.xml");

            Directory.CreateDirectory(Path.GetDirectoryName(linkXmlFilePath) ??
                                      throw new InvalidOperationException(
                                          $"No directory in file name {linkXmlFilePath}"));

            var assembliesToPreserve = Enumerable.Empty<string>()
                .Concat(GetDllAssemblyNames(assetsDir + _sourcesFolder))
                .Distinct()
                .OrderBy(s => s);

            var content = Enumerable.Empty<string>()
                .Concat("<linker>")
                .Concat(string.Empty)
                .Concat(assembliesToPreserve.Select(assemblyName =>
                    $"    <assembly fullname=\"{assemblyName}\" preserve=\"all\" />"))
                .Concat(string.Empty)
                .Concat("</linker>")
                .Aggregate(new StringBuilder(), (builder, line) => builder.AppendLine(line));

            using var fileStream = File.Open(linkXmlFilePath, FileMode.Create);
            using var streamWriter = new StreamWriter(fileStream);

            streamWriter.Write(content);
        }

        private static IEnumerable<string> GetDllAssemblyNames(string assetsDir)
        {
            return Directory.EnumerateFiles(assetsDir, "*.asmdef", SearchOption.AllDirectories)
                .Distinct()
                .Select(Path.GetFileNameWithoutExtension);
        }
    }

    public static class LinkerGeneratorExtensions
    {
        public static IEnumerable<TItem> Concat<TItem>(this IEnumerable<TItem> enumerable, TItem item) =>
            enumerable.Concat(new[] { item });
    }
AlonTalmi commented 1 year ago

I'm not sure there's something he can do about it, you can try for now adding [Preserve] attribute to the constructors. Maybe there's a way to generate a link.xml only including constructors in use

ooonush commented 4 months ago

Hi @AlonTalmi, I have a similar problem, only I am using SourceGenerator. Is it possible to mark all generated classes with [Preserve] attribute during generation? For example like this:

[Preserve]
private sealed class __GeneratedInjector : IInjector
hadashiA commented 3 months ago

AlonTalmi's comments may provide one answer.

Is it possible to mark all generated classes with [Preserve] attribute during generation?

Perhaps it is too much to mark all SourceGenerator target classes as Preserve at this time.

In fact, perhaps we should fix the filters that are subject to SourceGenerator first. Perhaps it would be sufficient to only target classes that have Register<T> or related declarations. After that, I think it would be ideal to make that limited target Preserve. #668 .

Alternatively, there is a proposal to create a LinkXml generator, as in #620.