예제 #1
0
 /// <summary>
 /// 初始化英战斗属性
 /// </summary>
 /// <param name="baseHero"></param>
 /// <param name="inscriptionPage"></param>
 public void InitBatteryAttibute(BaseHero baseHero, InscriptionPage inscriptionPage)
 {
     baseAttibute    = CopyTool.DeepCopy <BaseAttribute>(baseHero.baseAttribute);
     attackAttibute  = CopyTool.DeepCopy <AttackAttribute>(baseHero.attackAttribute);
     defenseAttibute = CopyTool.DeepCopy <DefenseAttribute>(baseHero.defenseAttribute);
     InitInscriptionAttibute(inscriptionPage);
 }
예제 #2
0
    public static NPath CopyIL2CPPMetadataFile(NPath destination, DotNetAssembly inputAssembly)
    {
        var target = destination.Combine("Data/Metadata/global-metadata.dat");

        CopyTool.Instance().Setup(target,
                                  Il2CppTargetDirForAssembly(inputAssembly).Combine("Data", "Metadata", "global-metadata.dat"));
        return(target);
    }
예제 #3
0
        public void AddRecommendEquipment(BaseEquipment baseEquipment)
        {
            BaseEquipment ecop = CopyTool.DeepCopy <BaseEquipment>(baseEquipment);

            if (_recommendList.ContainsKey(ecop.equipmentId))
            {
                _recommendList.Add(ecop.equipmentId, ecop);
            }
        }
예제 #4
0
 private static void CopyAllFilesFrom(NPath fromPath, NPath toPath)
 {
     foreach (var file in fromPath.Files(true))
     {
         CopyTool.Instance().Setup(
             toPath.Combine(file.RelativeTo(fromPath)).MakeAbsolute(),
             file.MakeAbsolute());
     }
 }
예제 #5
0
        public override BuildResult Run(BuildContext context)
        {
            var files = context.GetValue <AdditionallyProvidedFiles>().files;

            foreach (var entry in files)
            {
                CopyTool.Instance().Setup(entry.targetFile, entry.sourceFile);
            }
            return(context.Success());
        }
예제 #6
0
        public override void DoOtherSetup()
        {
            var sprite = skinningCache.GetSprites()[0];

            skinningCache.events.selectedSpriteChanged.Invoke(sprite);
            skinningCache.events.boneSelectionChanged.Invoke();

            m_CopyTool = skinningCache.GetTool(Tools.CopyPaste) as CopyTool;
            EditorGUIUtility.systemCopyBuffer = "";
        }
예제 #7
0
    public override void OnInspectorGUI()
    {
        CopyTool gen = (CopyTool)target;

        DrawDefaultInspector();

        if (GUILayout.Button("Copy"))
        {
            gen.Copy();
        }
    }
예제 #8
0
        private NPath[] SetupIL2CPPConversion(NPath[] linkerOutputFiles, NPath il2cppdata_destinationdir)
        {
            var il2cpp =
                new DotNetAssembly(
                    string.IsNullOrEmpty(CustomIL2CPPLocation) ?
                    $"{EditorApplication.applicationContentsPath}/il2cpp/build/deploy/net471/il2cpp.exe" :
                    $"{CustomIL2CPPLocation}/build/deploy/net471/il2cpp.exe",
                    Framework.Framework471);
            var netCoreRunRuntime = DotNetRuntime.FindFor(il2cpp); //NetCoreRunRuntime.FromSteve;
            var il2cppProgram     = new DotNetRunnableProgram(il2cpp, netCoreRunRuntime);

            var extraTypes = new HashSet <string>();

            foreach (var extraType in PlayerBuildInterface.ExtraTypesProvider?.Invoke() ?? Array.Empty <string>())
            {
                extraTypes.Add(extraType);
            }

            NPath extraTypesFile = Configuration.RootArtifactsPath.Combine("extra-types.txt").MakeAbsolute().WriteAllLines(extraTypes.ToArray());

            NPath il2cppOutputDir = Configuration.RootArtifactsPath.Combine("il2cpp");

            Backend.Current.AddAction("IL2CPP", Array.Empty <NPath>(),
                                      linkerOutputFiles
                                      .Concat(il2cpp.Path.Parent.Files()).Concat(netCoreRunRuntime.Inputs)
                                      .Concat(new[] { extraTypesFile })
                                      .ToArray(),
                                      il2cppProgram.InvocationString,
                                      new[]
            {
                "--convert-to-cpp",
                "--emit-null-checks",
                "--enable-array-bounds-check",
                "--dotnetprofile=\"unityaot\"",
                "--libil2cpp-static",
                $"--extra-types-file={extraTypesFile.InQuotes()}",
                "--profiler-report",
                $"--generatedcppdir={il2cppOutputDir.InQuotes(SlashMode.Native)}",
                $"--directory={linkerOutputFiles.First().Parent.InQuotes(SlashMode.Native)}",
            }, targetDirectories: new[] { il2cppOutputDir }, allowUnwrittenOutputFiles: true);

            var il2cppOutputFiles = GuessTargetDirectoryContentsFor(il2cppOutputDir, "dummy.cpp");

            var dataDir = il2cppOutputDir.Combine("Data");

            foreach (var il2cppdatafile in dataDir.FilesIfExists(recurse: true))
            {
                CopyTool.Instance().Setup(il2cppdata_destinationdir.Combine(il2cppdatafile.RelativeTo(dataDir)),
                                          il2cppdatafile);
            }

            return(il2cppOutputFiles);
        }
예제 #9
0
        public override void DoOtherSetup()
        {
            var sprite = skinningCache.GetSprites()[0];

            skinningCache.events.selectedSpriteChanged.Invoke(sprite);
            skinningCache.events.boneSelectionChanged.Invoke();

            m_CopyTool                     = skinningCache.GetTool(Tools.CopyPaste) as CopyTool;
            m_CopyStringStore              = new StringCopyToolStringStore();
            m_CopyStringStore.stringStore  = "";
            m_CopyTool.copyToolStringStore = m_CopyStringStore;
        }
예제 #10
0
 private static NPath Copy(NPath from, NPath to, ToolChain toolchain, string subFolderDir)
 {
     if (subFolderDir != string.Empty)
     {
         to = new NPath($"{pluginDir}/{subFolderDir}").Combine(to.FileName);
     }
     else
     {
         to = new NPath($"{pluginDir}/{toolchain.LegacyPlatformIdentifier}").Combine(to.FileName);
     }
     CopyTool.Instance().Setup(to, from);
     return(to);
 }
예제 #11
0
    public void SetTest()
    {
        List <string> strList = new List <string> {
            "test", "test", "test0", "test1"
        };

        Debug.Log("original");
        PrintStrList(strList);
        List <string> strSet = CopyTool.TransStrListToSet(strList);

        Debug.Log("after");
        PrintStrList(strSet);
    }
        private void CopyIcon(NPath destPath, string dpi, string iconName, string configIcon)
        {
            if (String.IsNullOrEmpty(configIcon))
            {
                return;
            }
            destPath = destPath.Combine($"src/main/res/mipmap-{dpi}", iconName);
            var srcPath = new NPath(configIcon);

            if (srcPath.IsRelative)
            {
                srcPath = (new NPath("../..")).Combine(srcPath);
            }
            m_projectFiles.Add(CopyTool.Instance().Setup(destPath, srcPath));
        }
    static void SetupGetEditorToolsFromStevedore()
    {
        // since this target and `get-editor-tools` target outputs the same files
        // we cannot have these two targets side by side in the dag.
        // We need this to generate the only correct target
        if (CompileEditorToolsFromSourceFileFlag.FileExists())
        {
            return;
        }

        var executablesFromEditorTools = new HashSet <string>
        {
            "artifacts/Stevedore/dots-editor-tools/images/osx/cwebp",
            "artifacts/Stevedore/dots-editor-tools/images/osx/moz-cjpeg",
            "artifacts/Stevedore/dots-editor-tools/images/osx/pngcrush",
            "artifacts/Stevedore/dots-editor-tools/manager/DotsEditorTools-macos",
        };

        var EditorTools = new StevedoreArtifact("dots-editor-tools");

        Backend.Current.Register(EditorTools);

        var dependencies = new List <NPath>();

        foreach (var file in EditorTools.GetFileList())
        {
            var target = new NPath(file.ToString().Replace(EditorTools.Path.ToString(), InstallationDirectory.ToString()));

            if ((HostPlatform.IsOSX || HostPlatform.IsLinux) && executablesFromEditorTools.Contains(file.ToString()))
            {
                Backend.Current.AddAction("copy and chmod +x", new[] { target }, new[] { file }, $"cp {file.InQuotes()} {target.InQuotes()} && chmod +x {target.InQuotes()}", Array.Empty <string>());
            }
            else
            {
                CopyTool.Instance().Setup(target, file);
            }

            dependencies.Add(target);
        }

        Backend.Current.AddAliasDependency("get-editor-tools", dependencies.ToArray());
    }
        private NPath CopyIcon(NPath srcPath, NPath destPath, string iconName, string configIcon)
        {
            const string iconsPath = "Sources/Assets.xcassets/AppIcon.appiconset";

            destPath = destPath.Combine(iconsPath, iconName);
            if (String.IsNullOrEmpty(configIcon))
            {
                srcPath = srcPath.Combine(iconsPath, iconName);
            }
            else
            {
                srcPath = new NPath(configIcon);
                if (srcPath.IsRelative)
                {
                    srcPath = (new NPath("../..")).Combine(srcPath);
                }
            }
            var iconPath = CopyTool.Instance().Setup(destPath, srcPath);

            m_projectFiles.Add(iconPath);
            return(iconPath);
        }
예제 #15
0
        public GraphVisualizationInfoView()
        {
            InitializeComponent();
            this.shapeInfoShapeMapping           = new BidirectionalDictionary <IShapeInfo, IShape>();
            this.connectionInfoConnectionMapping = new BidirectionalDictionary <IConnectionInfo, IConnection>();
            this.connectionPenStyle        = new LinePenStyle();
            this.connectionPenStyle.EndCap = LineCap.ArrowAnchor;

            PasteTool pasteTool = (PasteTool)this.Controller.Tools.Where(t => t.Name == ControllerBase.PasteToolName).FirstOrDefault();
            CopyTool  copyTool  = (CopyTool)this.Controller.Tools.Where(t => t.Name == ControllerBase.CopyToolName).FirstOrDefault();

            HeuristicLab.Netron.Controller controller = this.Controller as HeuristicLab.Netron.Controller;
            if (controller != null)
            {
                if (pasteTool != null)
                {
                    controller.RemoveTool(pasteTool);
                }
                if (copyTool != null)
                {
                    controller.RemoveTool(copyTool);
                }
            }
        }
        private void GenerateGradleProject(NPath gradleProjectPath)
        {
            var gradleSrcPath = AsmDefConfigFile.AsmDefDescriptionFor("Unity.Build.Android.DotsRuntime").Path.Parent.Combine("AndroidProjectTemplate~/");

            var hasGradleDependencies = false;
            var gradleDependencies    = new StringBuilder();

            gradleDependencies.AppendLine("    dependencies {");
            var hasKotlin = false;

            foreach (var d in Deployables.Where(d => (d is DeployableFile)))
            {
                var f = d as DeployableFile;
                if (f.Path.Extension == "aar" || f.Path.Extension == "jar")
                {
                    gradleDependencies.AppendLine($"        compile(name:'{f.Path.FileNameWithoutExtension}', ext:'{f.Path.Extension}')");
                    hasGradleDependencies = true;
                }
                else if (f.Path.Extension == "kt")
                {
                    hasKotlin = true;
                }
            }
            if (hasGradleDependencies)
            {
                gradleDependencies.AppendLine("    }");
            }
            else
            {
                gradleDependencies.Clear();
            }

            var kotlinClassPath = hasKotlin ? "        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.11'" : "";
            var kotlinPlugin    = hasKotlin ? "apply plugin: 'kotlin-android'" : "";

            var  loadLibraries = new StringBuilder();
            bool useStaticLib  = Deployables.FirstOrDefault(l => l.ToString().Contains("lib_unity_tiny_android.so")) == default(IDeployable);

            if (useStaticLib)
            {
                loadLibraries.AppendLine($"        System.loadLibrary(\"{m_gameName}\");");
            }
            else
            {
                var rx = new Regex(@".*lib([\w\d_]+)\.so", RegexOptions.Compiled);
                foreach (var l in Deployables)
                {
                    var match = rx.Match(l.ToString());
                    if (match.Success)
                    {
                        loadLibraries.AppendLine($"        System.loadLibrary(\"{match.Groups[1].Value}\");");
                    }
                }
            }

            String abiFilters = "";

            if (AndroidApkToolchain.Config.Architectures.Architectures == AndroidArchitecture.ARM64)
            {
                abiFilters = "'arm64-v8a'";
            }
            else if (AndroidApkToolchain.Config.Architectures.Architectures == AndroidArchitecture.ARMv7)
            {
                abiFilters = "'armeabi-v7a'";
            }
            else if (AndroidApkToolchain.IsFatApk)
            {
                abiFilters = "'armeabi-v7a', 'arm64-v8a'";
            }
            else // shouldn't happen
            {
                Console.WriteLine($"Tiny android toolchain doesn't support {AndroidApkToolchain.Config.Architectures.Architectures.ToString()} architectures");
            }

            // Android docs say "density" value was added in API level 17, but it doesn't compile with target SDK level lower than 24.
            string configChanges         = ((int)AndroidApkToolchain.Config.APILevels.ResolvedTargetAPILevel > 23) ? AndroidConfigChanges + "|density" : AndroidConfigChanges;
            var    useKeystore           = BuildConfiguration.HasComponent <AndroidKeystore>();
            var    renderOutsideSafeArea = BuildConfiguration.HasComponent <AndroidRenderOutsideSafeArea>();

            var icons                         = AndroidApkToolchain.Config.Icons;
            var hasBackground                 = icons.Icons.Any(i => !String.IsNullOrEmpty(i.Background));
            var hasCustomIcons                = hasBackground || icons.Icons.Any(i => !String.IsNullOrEmpty(i.Foreground) || !String.IsNullOrEmpty(i.Legacy));
            var version                       = AndroidApkToolchain.Config.Settings.Version;
            var versionFieldCount             = version.Revision > 0 ? 4 : 3;
            var maxRatio                      = AndroidApkToolchain.Config.AspectRatio.GetMaxAspectRatio(AndroidApkToolchain.Config.APILevels.ResolvedTargetAPILevel);
            var additionalApplicationMetadata = "";
            var additionalPermissions         = "";
            var additionalFeatures            = "";

            if (!String.IsNullOrEmpty(maxRatio))
            {
                additionalApplicationMetadata += GetMetaDataString("android.max_aspect", maxRatio);
            }
            if (BuildConfiguration.HasComponent <ARCoreSettings>())
            {
                additionalPermissions += GetPermissionString("android.permission.CAMERA");
                if (AndroidApkToolchain.Config.ARCore.Requirement == Requirement.Optional)
                {
                    additionalApplicationMetadata += "\n" + GetMetaDataString("com.google.ar.core", "optional");
                }
                else
                {
                    additionalApplicationMetadata += "\n" + GetMetaDataString("com.google.ar.core", "required");
                    additionalFeatures            += GetFeatureString("android.hardware.camera.ar", true);
                }
                if (AndroidApkToolchain.Config.ARCore.DepthSupport == Requirement.Required)
                {
                    additionalFeatures += "\n" + GetFeatureString("com.google.ar.core.depth", true);
                }
            }
            var templateStrings = new Dictionary <string, string>
            {
                { "**LOADLIBRARIES**", loadLibraries.ToString() },
                { "**PACKAGENAME**", AndroidApkToolchain.Config.Identifier.PackageName },
                { "**PRODUCTNAME**", AndroidApkToolchain.Config.Settings.ProductName },
                { "**VERSIONNAME**", version.ToString(versionFieldCount) },
                { "**VERSIONCODE**", AndroidApkToolchain.Config.VersionCode.VersionCode.ToString() },
                { "**ORIENTATION**", GetOrientationAttr() },
                { "**INSTALLLOCATION**", AndroidApkToolchain.Config.InstallLocation?.PreferredInstallLocationAsString() },
                { "**CUTOUTMODE**", AndroidRenderOutsideSafeArea.CutoutMode(renderOutsideSafeArea) },
                { "**NOTCHCONFIG**", AndroidRenderOutsideSafeArea.NotchConfig(renderOutsideSafeArea) },
                { "**NOTCHSUPPORT**", AndroidRenderOutsideSafeArea.NotchSupport(renderOutsideSafeArea) },
                { "**GAMENAME**", m_gameName },
                { "**MINSDKVERSION**", ((int)AndroidApkToolchain.Config.APILevels.MinAPILevel).ToString() },
                { "**TARGETSDKVERSION**", ((int)AndroidApkToolchain.Config.APILevels.ResolvedTargetAPILevel).ToString() },
                { "**CONFIGCHANGES**", configChanges },
                { "**ACTIVITY_ASPECT**", String.IsNullOrEmpty(maxRatio) ? "" : $"android:maxAspectRatio=\"{maxRatio}\"" },
                { "**ADDITIONAL_APPLICATION_METADATA**", additionalApplicationMetadata },
                { "**ADDITIONAL_PERMISSIONS**", additionalPermissions },
                { "**ADDITIONAL_FEATURES**", additionalFeatures },
                { "**ABIFILTERS**", abiFilters },
                { "**SIGN**", AndroidApkToolchain.Config.Keystore.GetSigningConfigs(useKeystore) },
                { "**SIGNCONFIG**", AndroidApkToolchain.Config.Keystore.GetSigningConfig(useKeystore) },
                { "**DEPENDENCIES**", gradleDependencies.ToString() },
                { "**KOTLINCLASSPATH**", kotlinClassPath },
                { "**KOTLINPLUGIN**", kotlinPlugin },
                { "**ALLOWED_PORTRAIT**", AndroidApkToolchain.AllowedOrientationPortrait ? "true" : "false" },
                { "**ALLOWED_REVERSE_PORTRAIT**", AndroidApkToolchain.AllowedOrientationReversePortrait ? "true" : "false" },
                { "**ALLOWED_LANDSCAPE**", AndroidApkToolchain.AllowedOrientationLandscape ? "true" : "false" },
                { "**ALLOWED_REVERSE_LANDSCAPE**", AndroidApkToolchain.AllowedOrientationReverseLandscape ? "true" : "false" },
                { "**BACKGROUND_PATH**", hasBackground ? "mipmap" : "drawable" }
            };

            // copy icon files
            if (hasCustomIcons)
            {
                for (int i = 0; i < icons.Icons.Length; ++i)
                {
                    var dpi = ((ScreenDPI)i).ToString().ToLower();
                    if (AndroidApkToolchain.Config.APILevels.TargetSDKSupportsAdaptiveIcons)
                    {
                        CopyIcon(gradleProjectPath, dpi, "ic_launcher_foreground.png", icons.Icons[i].Foreground);
                        CopyIcon(gradleProjectPath, dpi, "ic_launcher_background.png", icons.Icons[i].Background);
                    }
                    CopyIcon(gradleProjectPath, dpi, "app_icon.png", icons.Icons[i].Legacy);
                }
            }

            // copy and patch project files
            var apiRx = new Regex(@".+res[\\|\/].+-v([0-9]+)$", RegexOptions.Compiled);

            foreach (var r in gradleSrcPath.Files(true))
            {
                if ((hasCustomIcons && r.HasDirectory("mipmap-mdpi")) ||
                    (hasBackground && r.HasDirectory("drawable"))) // skipping icons files if there are custom ones
                {
                    continue;
                }
                if (!AndroidApkToolchain.Config.APILevels.TargetSDKSupportsAdaptiveIcons && r.FileName.StartsWith("ic_launcher_"))
                {
                    continue;
                }
                var match = apiRx.Match(r.Parent.ToString());
                if (match.Success)
                {
                    var api = Int32.Parse(match.Groups[1].Value);
                    if (api > (int)AndroidApkToolchain.Config.APILevels.ResolvedTargetAPILevel)
                    {
                        continue;
                    }
                }

                var destPath = gradleProjectPath.Combine(r.RelativeTo(gradleSrcPath));
                if (r.Extension == "template")
                {
                    destPath = destPath.ChangeExtension("");
                    var code = r.ReadAllText();
                    foreach (var t in templateStrings)
                    {
                        if (code.IndexOf(t.Key) != -1)
                        {
                            code = code.Replace(t.Key, t.Value);
                        }
                    }
                    Backend.Current.AddWriteTextAction(destPath, code);
                }
                else
                {
                    destPath = CopyTool.Instance().Setup(destPath, r);
                }
                m_projectFiles.Add(destPath);
            }

            var localProperties = new StringBuilder();

            localProperties.AppendLine($"sdk.dir={new NPath(AndroidApkToolchain.Config.ExternalTools.SdkPath).ToString()}");
            localProperties.AppendLine($"ndk.dir={new NPath(AndroidApkToolchain.Config.ExternalTools.NdkPath).ToString()}");
            var localPropertiesPath = gradleProjectPath.Combine("local.properties");

            Backend.Current.AddWriteTextAction(localPropertiesPath, localProperties.ToString());
            m_projectFiles.Add(localPropertiesPath);
        }
예제 #17
0
    private static DotsRuntimeCSharpProgram SetupGame(AsmDefDescription game)
    {
        var gameProgram = GetOrMakeDotsRuntimeCSharpProgramFor(game);

        var   withoutExt     = new NPath(gameProgram.FileName).FileNameWithoutExtension;
        NPath exportManifest = new NPath(withoutExt + "/export.manifest");

        Backend.Current.RegisterFileInfluencingGraph(exportManifest);
        if (exportManifest.FileExists())
        {
            var dataFiles = exportManifest.MakeAbsolute().ReadAllLines();
            foreach (var dataFile in dataFiles.Select(d => new NPath(d)))
            {
                gameProgram.SupportFiles.Add(new DeployableFile(dataFile, "Data/" + dataFile.FileName));
            }
        }

        var configToSetupGame = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>();

        if (!PerConfigBuildSettings.ContainsKey(game.Name))
        {
            return(null);
        }

        foreach (var config in PerConfigBuildSettings[game.Name].Where(config => !CanSkipSetupOf(game.Name, config)))
        {
            gameProgram.ProjectFile.StartInfo.Add(c => c == config,
                                                  StartInfoFor(config, EntryPointExecutableFor(gameProgram, config)));
            gameProgram.ProjectFile.BuildCommand.Add(c => c == config,
                                                     new BeeBuildCommand(GameDeployBinaryFor(gameProgram, config).ToString(), false, false).ToExecuteArgs());


            DotNetAssembly setupGame = gameProgram.SetupSpecificConfiguration(config).WithDeployables(new DotNetAssembly(
                                                                                                          Il2Cpp.Distribution.Path.Combine("build/profiles/Tiny/Facades/netstandard.dll"),
                                                                                                          Framework.FrameworkNone));

            var postILProcessedGame = ILPostProcessorTool.SetupInvocation(setupGame, config, gameProgram.Defines.For(config).ToArray());
            var postTypeRegGenGame  = TypeRegistrationTool.SetupInvocation(postILProcessedGame, config);
            configToSetupGame[config] = postTypeRegGenGame;
        }
        ;

        var il2CppOutputProgram = new Il2Cpp.Il2CppOutputProgram(gameProgram.AsmDefDescription.Name.Replace(".", "-") + ".il2cpp");

        var configToSetupGameBursted = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>();

        BurstCompiler hostBurstCompiler = null;

        if (HostPlatform.IsWindows)
        {
            hostBurstCompiler      = new BurstCompilerForWindows64();
            hostBurstCompiler.Link = true;
            hostBurstCompiler.OnlyStaticMethods = true;
        }
        else if (HostPlatform.IsOSX)
        {
            hostBurstCompiler      = new BurstCompilerForMac();
            hostBurstCompiler.Link = true;
            hostBurstCompiler.OnlyStaticMethods = true;
        }
        else
        {
            Console.WriteLine("Tiny burst not yet supported on linux.");
        }
        var webBurstCompiler = new BurstCompilerForEmscripten();

        foreach (var kvp in configToSetupGame)
        {
            var config    = kvp.Key;
            var setupGame = kvp.Value;

            if (config.UseBurst)
            {
                var outputDir = $"artifacts/{game.Name}/{config.Identifier}_bursted";
                var isWebGL   = config.Platform is WebGLPlatform;
                var extension = config.NativeProgramConfiguration.ToolChain.DynamicLibraryFormat.Extension;

                //burst generates a .bundle on os x.
                if (config.Platform is MacOSXPlatform)
                {
                    extension = "bundle";
                }

                var burstlib = BurstCompiler.SetupBurstCompilationAndLinkForAssemblies(
                    isWebGL
                        ? webBurstCompiler
                        : hostBurstCompiler,
                    setupGame,
                    new NPath(outputDir).Combine(
                        $"lib_burst_generated{("."+extension) ?? ""}"),
                    outputDir,
                    out var burstedGame);

                if (isWebGL)
                {
                    il2CppOutputProgram.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib);
                }
                else
                {
                    burstedGame = burstedGame.WithDeployables(burstlib);
                }
                configToSetupGameBursted[config] = burstedGame;
            }
            else
            {
                configToSetupGameBursted[config] = setupGame;
            }
        }

        var configToSetupGameStripped = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>();

        foreach (var kvp in configToSetupGameBursted)
        {
            var config    = kvp.Key;
            var setupGame = kvp.Value;

            if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp)
            {
                setupGame = Il2Cpp.UnityLinker.SetupInvocation(setupGame, $"artifacts/{game.Name}/{config.Identifier}_stripped", config.NativeProgramConfiguration);
                il2CppOutputProgram.SetupConditionalSourcesAndLibrariesForConfig(config, setupGame);
                configToSetupGameStripped[kvp.Key] = setupGame;
            }
            else
            {
                configToSetupGameStripped[kvp.Key] = kvp.Value;
            }
        }

        foreach (var kvp in configToSetupGameStripped)
        {
            var   config     = kvp.Key;
            var   setupGame  = kvp.Value;
            NPath deployPath = GameDeployDirectoryFor(gameProgram, config);

            IDeployable deployedGame;
            NPath       entryPointExecutable = null;

            if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp)
            {
                var builtNativeProgram = il2CppOutputProgram.SetupSpecificConfiguration(
                    config.NativeProgramConfiguration,
                    config.NativeProgramConfiguration.ExecutableFormat
                    )
                                         .WithDeployables(setupGame.RecursiveRuntimeDependenciesIncludingSelf.SelectMany(a => a.Deployables.Where(d => !(d is DotNetAssembly) && !(d is StaticLibrary)))
                                                          .ToArray());

                if (builtNativeProgram is IPackagedAppExtension)
                {
                    (builtNativeProgram as IPackagedAppExtension).SetAppPackagingParameters(gameProgram.AsmDefDescription.Name, config.NativeProgramConfiguration.CodeGen, gameProgram.SupportFiles.For(config));
                }
                deployedGame         = builtNativeProgram.DeployTo(deployPath);
                entryPointExecutable = deployedGame.Path;
                if (config.EnableManagedDebugging)
                {
                    Backend.Current.AddDependency(deployedGame.Path, Il2Cpp.CopyIL2CPPMetadataFile(deployPath, setupGame));
                }
            }
            else
            {
                deployedGame = setupGame.DeployTo(deployPath);

                var dotNetAssembly = (DotNetAssembly)deployedGame;

                //Usually a dotnet runtime game does not have a static void main(), and instead references another "entrypoint asmdef" that provides it.
                //This is convenient, but what makes it weird is that you have to start YourEntryPoint.exe  instead of YourGame.exe.   Until we have a better
                //solution for this, we're going to copy YourEntryPoint.exe to YourGame.exe, so that it's easier to find, and so that when it runs and you look
                //at the process name you understand what it is.
                if (deployedGame.Path.HasExtension("dll"))
                {
                    var to   = deployPath.Combine(deployedGame.Path.ChangeExtension("exe").FileName);
                    var from = dotNetAssembly.RecursiveRuntimeDependenciesIncludingSelf.SingleOrDefault(a => a.Path.HasExtension("exe"))?.Path;
                    if (from == null)
                    {
                        throw new InvalidProgramException($"Program {dotNetAssembly.Path} is an executable-like thing, but doesn't reference anything with Main");
                    }
                    Backend.Current.AddDependency(deployedGame.Path, CopyTool.Instance().Setup(to, from));
                    entryPointExecutable = to;
                }
                else
                {
                    entryPointExecutable = deployedGame.Path;
                }
            }

            //Because we use multidag, and try to not run all the setupcode when we just want to create projectfiles, we have a bit of a challenge.
            //Projectfiles require exact start and build commands. So we need to have a cheap way to calculate those. However, it's important that they
            //exactly match the actual place where the buildprogram is going to place our files. If these don't match things break down. The checks
            //in this block, they compare the "quick way to determine where the binary will be placed, and what the start executable is",  with the
            //actual return values returned from .DeployTo(), when we do run the actual buildcode.
            NPath deployedGamePath = GameDeployBinaryFor(gameProgram, config);
            if (deployedGame.Path != deployedGamePath)
            {
                throw new InvalidProgramException($"We expected deployPath to be {deployedGamePath}, but in reality it was {deployedGame.Path}");
            }
            var expectedEntryPointExecutable = EntryPointExecutableFor(gameProgram, config);
            if (entryPointExecutable != expectedEntryPointExecutable)
            {
                throw new InvalidProgramException($"We expected entryPointExecutable to be {expectedEntryPointExecutable}, but in reality it was {entryPointExecutable}");
            }

            Backend.Current.AddAliasDependency(config.Identifier, deployedGamePath);
        }

        return(gameProgram);
    }
예제 #18
0
        public BurstCompiler(string burstTarget, string burstPlatform, NPath outputDirectory)
        {
            BurstTarget     = burstTarget;
            BurstPlatform   = burstPlatform;
            OutputDirectory = outputDirectory;

            // On macOS the PlatformName is set as OSX in bee but it needs to be macOS for bcl.
            if (burstPlatform == "OSX")
            {
                BurstPlatform = "macOS";
            }

            if (burstPlatform == "IOS" && burstTarget == "ARMV8A_AARCH64")
            {
                BurstPlatform = "iOS";
            }

            NPath burstDir   = Path.GetFullPath("Packages/com.unity.burst/.Runtime");
            var   burstFiles = burstDir.Files(recurse: true);

            bool IsManagedBurstLibrary(NPath f)
            {
                if (!f.HasExtension(".dll"))
                {
                    return(false);
                }

                if (f.FileName.StartsWith("burst-llvm-"))
                {
                    return(false);
                }

                // These two libraries are not crossgen-compatible.
                if (f.FileName == "Unity.Cecil.Rocks.dll" || f.FileName == "Newtonsoft.Json.dll")
                {
                    return(false);
                }

                return(true);
            }

            var bclAssembly = new DotNetAssembly(burstDir.Combine("bcl.exe"), Framework.Framework471)
                              .WithRuntimeDependencies(burstFiles
                                                       .Where(IsManagedBurstLibrary)
                                                       .Select(f => new DotNetAssembly(f, Framework.Framework471))
                                                       .ToArray());

#if BURST_NETCORE
            //todo: turn this to true.  we cannot right now because NetCoreRuntime.SteveDore is implemented through a static field,
            //which is incompatible with our "create graph in editor, and maybe we will create two graphs during the same domain".  the creation
            //of the steve artifact happens only once,  but it needs to be registered in each backend. the fact that it doesn't means you can get into
            //ugly situations where on second builds the dependencies to netcorerun are not properly setup.
            bool useNetCore = false;

            if (useNetCore)
            {
                var runtime = NetCoreRunRuntime.FromSteve;
                _dotnetRuntime = runtime;

                var useCrossGen = false;
                if (useCrossGen)
                {
                    bclAssembly = runtime.SetupAheadOfTimeCompilation(bclAssembly, "artifacts/bcl-crossgen");

                    bclAssembly = bclAssembly.WithPath(bclAssembly.Path.MakeAbsolute(BeeProjectRoot));
                    foreach (var file in burstFiles.Where(x => !IsManagedBurstLibrary(x)))
                    {
                        var relative = file.RelativeTo(burstDir);

                        var temp = CopyTool.Instance().Setup(bclAssembly.Path.Parent.Combine(relative), file);
                        Backend.Current.AddDependency(temp, bclAssembly.Path);
                    }
                }
            }
            else
#else
            {
                _dotnetRuntime = DotNetRuntime.FindFor(bclAssembly);
            }
#endif

            { _burstRunnableProgram = new DotNetRunnableProgram(bclAssembly, _dotnetRuntime); }
            _burstCompilerInputFiles = burstFiles;

            var burstPackageInfo =
                UnityEditor.PackageManager.PackageInfo.FindForAssetPath("Packages/com.unity.burst/somefile");
            var burstPackageRawVersion = burstPackageInfo.version;

            // Remove everything after '-' if this is a preview package.
            var parts = burstPackageRawVersion.Split('-');
            if (parts.Length > 1)
            {
                burstPackageRawVersion = parts[0];
            }

            // Get the preview version number.
            // Default to max int to signal a very high preview number on release.
            var previewNumber = int.MaxValue;
            if (parts.Length > 1)
            {
                previewNumber = int.Parse(parts[1].Split('.')[1]);
            }

            var burstVersion = Version.Parse(burstPackageRawVersion);
            var burstVersionWithIncludeRootAssemblyReferencesFeature = new Version(1, 3);
            _installedBurstSupportsIncludeRootAssemblyReferencesFeature = burstVersion >= burstVersionWithIncludeRootAssemblyReferencesFeature;
            if (!_installedBurstSupportsIncludeRootAssemblyReferencesFeature)
            {
                Debug.Log($"Using burst 1.3 instead of {burstVersion.ToString()} will give much better build times. At the time of this writing it is only available by building manually on your machine.");
            }

            _installedBurstSupportsCaching =
                (burstVersion >= new Version(1, 3) && previewNumber > 7) ||
                burstVersion >= new Version(1, 4);
            if (!_installedBurstSupportsCaching)
            {
                Debug.Log($"Using burst 1.3 preview 8 or above instead of {burstVersion.ToString()} will give much better build times. At the time of this writing it is only available by building manually on your machine.");
            }

            _installedBurstIs1_3Preview10OrLater =
                (burstVersion >= new Version(1, 3) && previewNumber >= 10) ||
                burstVersion >= new Version(1, 4);
        }
        private NPath PackageApp(NPath buildPath, BuiltNativeProgram mainProgram)
        {
            var mainLibPath = mainProgram.Path;

            m_projectFiles.Add(mainLibPath);
            m_projectFiles.AddRange(mainProgram.Deployables.Select(d => d.Path));

            if (m_apkToolchain == null)
            {
                Console.WriteLine($"Error: not Android APK toolchain");
                return(buildPath);
            }
            if (AndroidApkToolchain.ExportProject)
            {
                var deployedPath = buildPath.Combine(m_gameName);
                GenerateGradleProject(deployedPath);

                // stub action to have deployedPath in build tree and set correct dependencies
                Backend.Current.AddAction(
                    actionName: "Gradle project folder",
                    targetFiles: new[] { deployedPath },
                    inputs: m_projectFiles.ToArray(),
                    executableStringFor: $"echo created",
                    commandLineArguments: Array.Empty <string>(),
                    allowUnexpectedOutput: true,
                    allowUnwrittenOutputFiles: true
                    );
                return(deployedPath);
            }
            else
            {
                var deployedPath      = buildPath.Combine(m_gameName + "." + m_apkToolchain.ExecutableFormat.Extension);
                var gradleProjectPath = mainLibPath.Parent.Parent.Parent.Parent.Parent;
                GenerateGradleProject(gradleProjectPath);
                var pathToRoot = new NPath(string.Concat(Enumerable.Repeat("../", gradleProjectPath.Depth)));

                var javaLaunchPath   = new NPath(AndroidApkToolchain.Config.ExternalTools.JavaPath).Combine("bin").Combine("java");
                var gradleLaunchPath = AndroidApkToolchain.GetGradleLaunchJarPath();
                var releaseBuild     = m_config == DotsConfiguration.Release;
                var gradleCommand    = AndroidApkToolchain.BuildAppBundle ?
                                       (releaseBuild ? "bundleRelease" : "bundleDebug") :
                                       (releaseBuild ? "assembleRelease" : "assembleDebug");
                var gradleExecutableString = $"cd {gradleProjectPath.InQuotes()} && {javaLaunchPath.InQuotes()} -classpath {gradleLaunchPath.InQuotes()} org.gradle.launcher.GradleMain {gradleCommand} && cd {pathToRoot.InQuotes()}";

                var config          = releaseBuild ? "release" : "debug";
                var gradleBuildPath = gradleProjectPath.Combine("build/outputs").
                                      Combine(AndroidApkToolchain.BuildAppBundle ? "bundle" : "apk").
                                      Combine($"{config}/gradle-{config}.{(AndroidApkToolchain.BuildAppBundle ? "aab" : "apk")}");

                Backend.Current.AddAction(
                    actionName: "Build Gradle project",
                    targetFiles: new[] { gradleBuildPath },
                    inputs: m_projectFiles.ToArray(),
                    executableStringFor: gradleExecutableString,
                    commandLineArguments: Array.Empty <string>(),
                    allowUnexpectedOutput: false,
                    allowedOutputSubstrings: new[] { ":*", "BUILD SUCCESSFUL in *" }
                    );

                return(CopyTool.Instance().Setup(deployedPath, gradleBuildPath));
            }
        }
예제 #20
0
        public NPath PackageApp(NPath buildPath, NPath mainLibPath)
        {
            if (m_iosAppToolchain == null)
            {
                Console.WriteLine("Error: not IOS App toolchain");
                return(mainLibPath);
            }

            var iosPlatformPath  = AsmDefConfigFile.AsmDefDescriptionFor("Unity.Platforms.iOS").Path.Parent;
            var xcodeProjectPath = mainLibPath.Parent;
            var xcodeSrcPath     = iosPlatformPath.Combine(TinyProjectName + "~");
            var xcodeprojPath    = xcodeProjectPath.Combine($"{TinyProjectName}.xcodeproj");

            // copy and patch pbxproj file
            var pbxPath            = xcodeprojPath.Combine("project.pbxproj");
            var pbxTemplatePath    = xcodeSrcPath.Combine($"{TinyProjectName}.xcodeproj", "project.pbxproj");
            var exportManifestPath = new NPath(m_gameName).Combine("export.manifest");
            var result             = SetupXCodeProject(pbxTemplatePath, exportManifestPath.FileExists());

            Backend.Current.AddWriteTextAction(pbxPath, result);
            Backend.Current.AddDependency(pbxPath, mainLibPath);

            // copy and patch Info.plist file
            var plistPath         = xcodeProjectPath.Combine("Sources", "Info.plist");
            var plistTemplatePath = xcodeSrcPath.Combine("Sources", "Info.plist");

            result = SetupInfoPlist(plistTemplatePath);
            Backend.Current.AddWriteTextAction(plistPath, result);
            Backend.Current.AddDependency(pbxPath, plistPath);

            // copy xcodeproj files
            foreach (var r in xcodeSrcPath.Files(true))
            {
                if (r.Extension != "pbxproj" && r.FileName != "Info.plist")
                {
                    var destPath = xcodeProjectPath.Combine(r.RelativeTo(xcodeSrcPath));
                    destPath = CopyTool.Instance().Setup(destPath, r);
                    Backend.Current.AddDependency(pbxPath, destPath);
                }
            }

            foreach (var r in m_supportFiles)
            {
                if (r.Path.FileName == "testconfig.json")
                {
                    Backend.Current.AddDependency(pbxPath, CopyTool.Instance().Setup(buildPath.Combine(r.Path.FileName), r.Path));
                    break;
                }
            }

            // TODO probably it is required to keep previous project since it can be modified by user
            var outputPath = buildPath.Combine($"{m_gameName}");

            Console.WriteLine($"Move project to {outputPath}");
            Backend.Current.AddAction(
                actionName: "Open XCode project folder",
                targetFiles: new[] { outputPath },
                inputs: new[] { pbxPath },
                executableStringFor: $"rm -rf {outputPath} && mv {xcodeProjectPath} {outputPath} && open {outputPath}",
                commandLineArguments: Array.Empty <string>(),
                allowUnexpectedOutput: true
                );

            return(outputPath);
        }
예제 #21
0
        public NPath[] Setup(Architecture architecture, DotNetAssembly[] inputAssemblies, int assemblyIndex, string burstLibraryName, Dictionary <string, string> environmentVariables, BurstOutputMode outputMode)
        {
            using (new ProfilerMarker("Burst-" + burstLibraryName).Auto())
            {
                NPath intermediateOutputDir = Configuration.RootArtifactsPath.Combine($"bcl/{architecture.Name}/{burstLibraryName}");

                var runtimeDependencies = inputAssemblies.SelectMany(i =>
                {
                    return(i.RecursiveRuntimeDependenciesIncludingSelf);
                })
                                          .Distinct().ToArray();

                var args = new List <string>
                {
                    $"--target={BurstTarget}",
                    $"--platform={BurstPlatform}",
                    //"--log-timings",
                    "--dump=none",
                    $"--output={intermediateOutputDir.Combine(burstLibraryName)}",
                    inputAssemblies.Select(inputAssembly => $"--root-assembly={inputAssembly.Path.InQuotesResolved()}"),
                    runtimeDependencies.Select(r => r.Path.ResolveWithFileSystem().Parent.InQuotes()).Distinct().Select(d => $"--assembly-folder={d}"),
                };

                if (_installedBurstSupportsIncludeRootAssemblyReferencesFeature)
                {
                    args.Add("--include-root-assembly-references=false");
                }

                if (_installedBurstSupportsCaching)
                {
                    args.Add("--always-create-output=false");

                    if (outputMode == BurstOutputMode.LibraryPerJob)
                    {
                        args.Add("--output-mode=LibraryPerJob");
                        args.Add(
                            $"--cache-directory={NPath.CurrentDirectory.Combine("Library/BurstCache/Incremental")}");
                    }
                }

                if (!_installedBurstIs1_3Preview10OrLater)
                {
                    args.Add($"--mintarget={BurstTarget}");
                }

                if (_installedBurstIs1_3Preview10OrLater)
                {
                    args.Add("--debug=Full");
                }
                else
                {
                    args.Add("--debug");
                }

                Backend.Current.AddAction(
                    "Burst"
                    , new NPath[]
                {
                }
                    , runtimeDependencies.Select(p => p.Path).Concat(_dotnetRuntime.Inputs)
                    .Concat(_burstCompilerInputFiles).ToArray()
                    , executableStringFor: _burstRunnableProgram.InvocationString
                    , args.ToArray()
                    , targetDirectories: new[] { intermediateOutputDir }
                    , environmentVariables: environmentVariables
                    );

                // This file extension thingy doesn't scale at all
                NPath[] producedFiles;

                using (new ProfilerMarker("Globbing produced files").Auto())
                {
                    producedFiles = intermediateOutputDir.FilesIfExists("*.dll")
                                    .Concat(intermediateOutputDir.FilesIfExists("*.so"))
                                    .Concat(intermediateOutputDir.FilesIfExists("*.a"))
                                    .Concat(intermediateOutputDir.FilesIfExists("*.bundle"))
                                    .ToArray();
                }

                int jobCounter  = 0;
                var targetFiles = new List <NPath>();
                foreach (var producedFile in producedFiles)
                {
                    var targetFile = OutputDirectory.Combine($"lib_burst_{assemblyIndex}_{jobCounter++}").ChangeExtension(producedFile.Extension);
                    targetFiles.Add(CopyTool.Instance().Setup(targetFile, producedFile));

                    if (BurstPlatform == "Windows" && inputAssemblies.Any(x => x.DebugSymbolPath != null))
                    {
                        const string debugExtension = ".pdb"; // TODO: Handle other platforms.

                        var producedDebugSymbols = producedFile.ChangeExtension(debugExtension);

                        var targetDebugSymbols = targetFile.ChangeExtension(debugExtension);
                        targetFiles.Add(CopyTool.Instance().Setup(targetDebugSymbols, producedDebugSymbols));
                    }
                }

                return(targetFiles.ToArray());
            }
        }
예제 #22
0
    private static DotsRuntimeCSharpProgram SetupGame(AsmDefDescription game)
    {
        DotsRuntimeCSharpProgram gameProgram = GetOrMakeDotsRuntimeCSharpProgramFor(game);

        var   withoutExt     = new NPath(gameProgram.FileName).FileNameWithoutExtension;
        NPath exportManifest = new NPath(withoutExt + "/export.manifest");

        Backend.Current.RegisterFileInfluencingGraph(exportManifest);
        if (exportManifest.FileExists())
        {
            var dataFiles = exportManifest.MakeAbsolute().ReadAllLines();
            foreach (var dataFile in dataFiles.Select(d => new NPath(d)))
            {
                gameProgram.SupportFiles.Add(new DeployableFile(dataFile, "Data/" + dataFile.FileName));
            }
        }

        var configToSetupGame = DotsConfigs.Configs.ToDictionary(config => config, config =>
        {
            DotNetAssembly setupGame = gameProgram.SetupSpecificConfiguration(config);
            return(TypeRegistrationTool.SetupInvocation(setupGame, config));
        });

        var il2CppOutputProgram = new Il2Cpp.Il2CppOutputProgram(gameProgram.FileName + "_il2cpp");

        foreach (var kvp in configToSetupGame)
        {
            var config    = kvp.Key;
            var setupGame = kvp.Value;

            if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp)
            {
                setupGame = Il2Cpp.UnityLinker.SetupInvocation(setupGame, $"artifacts/{game.Name}/{config.Identifier}_stripped", config.NativeProgramConfiguration);
                il2CppOutputProgram.SetupConditionalSourcesAndLibrariesForConfig(config, setupGame);
            }
        }

        foreach (var kvp in configToSetupGame)
        {
            var   config     = kvp.Key;
            var   setupGame  = kvp.Value;
            NPath deployPath = $"build/{game.Name}/{game.Name}-{config.Identifier}";

            IDeployable deployedGame;

            if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp)
            {
                var builtNativeProgram = il2CppOutputProgram.SetupSpecificConfiguration(
                    config.NativeProgramConfiguration,
                    config.NativeProgramConfiguration.ExecutableFormat
                    )
                                         .WithDeployables(setupGame.RecursiveRuntimeDependenciesIncludingSelf.SelectMany(a => a.Deployables.Where(d => !(d is DotNetAssembly) && !(d is StaticLibrary)))
                                                          .ToArray());

                deployedGame = builtNativeProgram.DeployTo(deployPath);
            }
            else
            {
                deployedGame = setupGame.DeployTo(deployPath);

                var dotNetAssembly = (DotNetAssembly)deployedGame;

                //Usually a dotnet runtime game does not have a static void main(), and instead references another "entrypoint asmdef" that provides it.
                //This is convenient, but what makes it weird is that you have to start YourEntryPoint.exe  instead of YourGame.exe.   Until we have a better
                //solution for this, we're going to copy YourEntryPoint.exe to YourGame.exe, so that it's easier to find, and so that when it runs and you look
                //at the process name you understand what it is.
                if (deployedGame.Path.HasExtension("dll"))
                {
                    var to   = deployPath.Combine(deployedGame.Path.ChangeExtension("exe").FileName);
                    var from = dotNetAssembly.RecursiveRuntimeDependenciesIncludingSelf.Single(a => a.Path.HasExtension("exe")).Path;
                    Backend.Current.AddDependency(deployedGame.Path, CopyTool.Instance().Setup(to, from));
                }
            }

            NPath deployedGamePath = deployedGame.Path;

            gameProgram.ProjectFile.StartInfo.Add(c => c == config, StartInfoFor(config, deployedGame));
            gameProgram.ProjectFile.BuildCommand.Add(c => c == config, new BeeBuildCommand(deployedGamePath.ToString(), false, false).ToExecuteArgs());

            Backend.Current.AddAliasDependency($"{game.Name.ToLower()}-{config.Identifier}", deployedGamePath);
            Backend.Current.AddAliasDependency($"{game.Name.ToLower()}-all", deployedGamePath);
        }

        return(gameProgram);
    }
    static void SetupCompileEditorTools(NPath rootPath)
    {
        // since this target and `get-editor-tools` target outputs the same files
        // we cannot have these two targets side by side in the dag.
        // We need this to generate the only correct target
        if (!CompileEditorToolsFromSourceFileFlag.FileExists())
        {
            return;
        }

        var editorToolsSourceDirectory = rootPath.Combine("EditorTools/Src");
        var env = new Dictionary <string, string>()
        {
            { "PATH", $"{NodeDirectory.ToString()}{PathSeparator}{Environment.GetEnvironmentVariable("PATH")}" }
        };
        var dependencies = new List <NPath>();

        // Iterate all folders in Tools and process them
        foreach (var toolDir in editorToolsSourceDirectory.Contents())
        {
            if (toolDir.FileExists("package.json"))
            {
                var packageLockJsonFilePath = toolDir.Combine("package-lock.json");
                var packageJsonFilePath     = toolDir.Combine("package.json");

                // Run npm install
                Backend.Current.AddAction($"npm install",
                                          targetFiles: new[] { packageLockJsonFilePath },
                                          inputs: new[] { Node.Path, packageJsonFilePath },
                                          executableStringFor: $"cd {toolDir.InQuotes()} && npm install",
                                          commandLineArguments: Array.Empty <string>(),
                                          environmentVariables: env,
                                          allowUnwrittenOutputFiles: true);

                dependencies.Add(packageLockJsonFilePath);

                // Run package
                var inputs = new List <NPath>
                {
                    Node.Path,
                    packageLockJsonFilePath
                };

                var indexJsNotInModules = toolDir.Files("index.js", true).Where(p => !p.IsChildOf(toolDir.Combine("node_modules")));
                inputs.AddRange(indexJsNotInModules);
                var toolInstallDir = InstallationDirectory.Combine(toolDir.FileName);

                Backend.Current.AddAction($"package",
                                          targetFiles: new[] { toolInstallDir.Combine($"DotsEditorTools-win.exe"), toolInstallDir.Combine($"DotsEditorTools-macos") },
                                          inputs: inputs.ToArray(),
                                          executableStringFor: $"cd {toolDir.InQuotes()} && npm run package -- --out-path {toolInstallDir.InQuotes()} --targets win-x64,macos-x64 .",
                                          commandLineArguments: Array.Empty <string>(),
                                          environmentVariables: env,
                                          allowUnwrittenOutputFiles: true);

                dependencies.Add(toolInstallDir.Combine($"DotsEditorTools-win.exe"));
                dependencies.Add(toolInstallDir.Combine($"DotsEditorTools-macos"));
            }
            else // Not a node tool, just copy files recursively
            {
                foreach (var file in toolDir.Files(true))
                {
                    if (file.FileName == "extrabeetmpfile")
                    {
                        continue;
                    }

                    var target = file.ToString().Replace(editorToolsSourceDirectory.ToString(), InstallationDirectory.ToString());
                    CopyTool.Instance().Setup(target, file);
                    dependencies.Add(target);
                }
            }
        }

        Backend.Current.AddAliasDependency("compile-editor-tools", dependencies.ToArray());
    }
예제 #24
0
    private static DotsRuntimeCSharpProgram SetupGame(AsmDefDescription game)
    {
        var gameProgram       = GetOrMakeDotsRuntimeCSharpProgramFor(game);
        var configToSetupGame = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>();

        if (!PerConfigBuildSettings.ContainsKey(game.Name))
        {
            return(null);
        }

        var configsToUse = PerConfigBuildSettings[game.Name].Where(config => !CanSkipSetupOf(game.Name, config));

        foreach (var config in configsToUse)
        {
            var   withoutExt     = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier);
            NPath exportManifest = withoutExt.Combine("export.manifest");
            Backend.Current.RegisterFileInfluencingGraph(exportManifest);
            if (exportManifest.FileExists())
            {
                var dataFiles = exportManifest.MakeAbsolute().ReadAllLines();
                foreach (var dataFile in dataFiles.Select(d => new NPath(d)))
                {
                    gameProgram.SupportFiles.Add(
                        c => c.Equals(config),
                        new DeployableFile(dataFile, GetDeployPathFromExportPath(dataFile)));
                }
            }

            gameProgram.ProjectFile.StartInfo.Add(
                c => c == config,
                StartInfoFor(config, EntryPointExecutableFor(gameProgram, config)));
            gameProgram.ProjectFile.BuildCommand.Add(
                c => c == config,
                new BeeBuildCommand(GameDeployBinaryFor(gameProgram, config).ToString(), false, false).ToExecuteArgs());
        }

        foreach (var config in configsToUse)
        {
            DotNetAssembly setupGame = gameProgram.SetupSpecificConfiguration(config);

            if (config.TargetFramework == TargetFramework.Tiny)
            {
                var tinyStandard = new DotNetAssembly(Il2Cpp.Distribution.Path.Combine("build/profiles/Tiny/Facades/netstandard.dll"), Framework.FrameworkNone);
                setupGame = setupGame.WithDeployables(tinyStandard);
            }

            var postILProcessedGame = ILPostProcessorTool.SetupInvocation(
                setupGame,
                config,
                gameProgram.Defines.For(config).ToArray());

            var postTypeRegGenGame = TypeRegistrationTool.SetupInvocation(postILProcessedGame, config);
            configToSetupGame[config] = postTypeRegGenGame;
        }

        var il2CppOutputProgram = new Il2Cpp.Il2CppOutputProgram(gameProgram.AsmDefDescription.Name);

        var configToSetupGameBursted = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>();

        foreach (var kvp in configToSetupGame)
        {
            var config    = kvp.Key;
            var setupGame = kvp.Value;

            if (config.UseBurst)
            {
                BurstCompiler burstCompiler = null;
                if (config.Platform is WindowsPlatform)
                {
                    burstCompiler      = new BurstCompilerForWindows64();
                    burstCompiler.Link = false;
                }
                else if (config.Platform is MacOSXPlatform)
                {
                    burstCompiler      = new BurstCompilerForMac();
                    burstCompiler.Link = false;
                }
                else if (config.Platform is IosPlatform)
                {
                    burstCompiler = new BurstCompilerForiOS();
                    burstCompiler.EnableStaticLinkage = true;
                    burstCompiler.ObjectFileExtension = "a";
                }
                else if (config.Platform is LinuxPlatform)
                {
                    burstCompiler = new BurstCompilerForLinuxWaitingForBurstRelease();
                }
                else if (config.Platform is AndroidPlatform)
                {
                    burstCompiler = new BurstCompilerForAndroid();
                    burstCompiler.EnableStaticLinkage = false;
                    burstCompiler.Link = false;
                    burstCompiler.EnableDirectExternalLinking = true;
                    if (config.NativeProgramConfiguration.ToolChain.Architecture is Arm64Architecture)
                    {
                        burstCompiler.TargetArchitecture = "ARMV8A_AARCH64";
                    }
                }
                else if (config.Platform is WebGLPlatform)
                {
                    burstCompiler = new BurstCompilerForEmscripten();
                    burstCompiler.EnableStaticLinkage = true;
                    burstCompiler.DisableVectors      = false;
                }

                // Only generate marshaling info for platforms that require marshalling (e.g. Windows DotNet)
                // but also if collection checks are enabled (as that is why we need marshalling)
                burstCompiler.EnableJobMarshalling &= config.EnableUnityCollectionsChecks;
                burstCompiler.SafetyChecks          = config.EnableUnityCollectionsChecks;
                burstCompiler.DisableWarnings       = "BC1370"; // Suppress warning for burst function throwing an exception

                var            outputDir    = $"artifacts/{game.Name}/{config.Identifier}_bursted";
                var            burstLibName = "lib_burst_generated";
                DotNetAssembly burstedGame  = setupGame;

                var burstlib = BurstCompiler.SetupBurstCompilationForAssemblies(
                    burstCompiler,
                    setupGame,
                    new NPath(outputDir).Combine("bclobj"),
                    outputDir,
                    burstLibName,
                    out burstedGame);
                if ((config.Platform is IosPlatform || config.Platform is AndroidPlatform) &&
                    config.NativeProgramConfiguration.ToolChain.DynamicLibraryFormat.Extension == "a") // static lib based toolchain
                {
                    il2CppOutputProgram.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib);
                    il2CppOutputProgram.Defines.Add(
                        c => c.Equals(config.NativeProgramConfiguration),
                        $"FORCE_PINVOKE_{burstLibName}_INTERNAL");
                }
                else if (config.Platform is WebGLPlatform)
                {
                    il2CppOutputProgram.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib);
                }
                else
                {
                    var burstDynamicLib = new NativeProgram(burstLibName);
                    burstDynamicLib.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib);
                    burstDynamicLib.Libraries.Add(
                        c => c.Equals(config.NativeProgramConfiguration),
                        gameProgram.TransitiveReferencesFor(config)
                        .Where(
                            p => p is DotsRuntimeCSharpProgram &&
                            ((DotsRuntimeCSharpProgram)p).NativeProgram != null)
                        .Select(
                            p => new NativeProgramAsLibrary(((DotsRuntimeCSharpProgram)p).NativeProgram)
                    {
                        BuildMode = NativeProgramLibraryBuildMode.Dynamic
                    }));

                    if (config.Platform is IosPlatform || config.Platform is AndroidPlatform)
                    {
                        NativeJobsPrebuiltLibrary.AddToNativeProgram(burstDynamicLib);
                    }

                    DotsRuntimeCSharpProgram.SetupDotsRuntimeNativeProgram(burstLibName, burstDynamicLib);

                    var builtBurstLib = burstDynamicLib.SetupSpecificConfiguration(
                        config.NativeProgramConfiguration,
                        config.NativeProgramConfiguration.ToolChain.DynamicLibraryFormat);
                    burstedGame = burstedGame.WithDeployables(builtBurstLib);
                }

                configToSetupGameBursted[config] = burstedGame;
            }
            else
            {
                configToSetupGameBursted[config] = setupGame;
            }
        }

        var configToSetupGameStripped = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>();

        foreach (var kvp in configToSetupGameBursted)
        {
            var config    = kvp.Key;
            var setupGame = kvp.Value;

            if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp)
            {
                setupGame = Il2Cpp.UnityLinker.SetupInvocation(setupGame, $"artifacts/{game.Name}/{config.Identifier}_stripped", config.NativeProgramConfiguration);
                il2CppOutputProgram.SetupConditionalSourcesAndLibrariesForConfig(config, setupGame);
                configToSetupGameStripped[kvp.Key] = setupGame;
            }
            else
            {
                configToSetupGameStripped[kvp.Key] = kvp.Value;
            }
        }

        foreach (var kvp in configToSetupGameStripped)
        {
            var   config     = kvp.Key;
            var   setupGame  = kvp.Value;
            NPath deployPath = GameDeployDirectoryFor(gameProgram, config);

            IDeployable deployedGame;
            NPath       entryPointExecutable = null;

            if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp)
            {
                var   tinyShellFileName = "tiny_shell.html";
                NPath tinyShellPath     = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier, "WebTemplate", tinyShellFileName);
                il2CppOutputProgram.DynamicLinkerSettingsForEmscripten().Add(c => c.WithShellFile(tinyShellPath));

                var builtNativeProgram = il2CppOutputProgram.SetupSpecificConfiguration(
                    config.NativeProgramConfiguration,
                    config.NativeProgramConfiguration.ExecutableFormat
                    )
                                         .WithDeployables(setupGame.RecursiveRuntimeDependenciesIncludingSelf.SelectMany(a => a.Deployables.Where(d => !(d is DotNetAssembly) && !(d is StaticLibrary)))
                                                          .ToArray());

                if (builtNativeProgram is IPackagedAppExtension)
                {
                    (builtNativeProgram as IPackagedAppExtension).SetAppPackagingParameters(
                        gameProgram.AsmDefDescription.Name, config.DotsConfiguration);
                }

                if (config.PlatformBuildConfig is WebBuildConfig webBuildConfig)
                {
                    if (webBuildConfig.SingleFile)
                    {
                        deployedGame = new DeployableFile(GameDeployBinaryFor(gameProgram, config));
                        CopyTool.Instance().Setup(deployedGame.Path, (builtNativeProgram as EmscriptenExecutable).Path);
                    }
                    else
                    {
                        deployedGame = builtNativeProgram.DeployTo(deployPath);
                    }

                    var webTemplateFolder = webBuildConfig.WebTemplateFolder;
                    if (String.IsNullOrEmpty(webTemplateFolder))
                    {
                        webTemplateFolder = LowLevelRoot.Combine("WebSupport", "WebTemplates", "Default").ToString();
                    }
                    if (new NPath(webTemplateFolder).IsRelative)
                    {
                        webTemplateFolder = new NPath("../..").Combine(webTemplateFolder).MakeAbsolute().ToString();
                    }
                    if (!new NPath(webTemplateFolder).Combine(tinyShellFileName).FileExists())
                    {
                        throw new InvalidProgramException($"Web template folder \"{webTemplateFolder}\" doesn't contain \"{tinyShellFileName}\" file.");
                    }

                    foreach (var templateFilePath in new NPath(webTemplateFolder).Files(recurse:true))
                    {
                        string fileRelativePath = templateFilePath.ToString().Substring(webTemplateFolder.Length + 1);
                        if (fileRelativePath == tinyShellFileName)
                        {
                            NPath shellPackager = LowLevelRoot.Combine("WebSupport", "package_shell_file.js");
                            NPath tinyShellJS   = LowLevelRoot.Combine("WebSupport", "tiny_shell.js");
                            var   inputs        = new List <NPath> {
                                TinyEmscripten.NodeExe, shellPackager, templateFilePath, tinyShellJS
                            };
                            var commandLineArguments = new List <string> {
                                shellPackager.ToString(), "--outputHtml", tinyShellPath.ToString(), "--inputShellHtml", templateFilePath.ToString(), "--inputShellJs", tinyShellJS.ToString()
                            };
                            NPath exportManifest = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier, "export.manifest");
                            if (webBuildConfig.SingleFile && exportManifest.FileExists())
                            {
                                inputs.Add(exportManifest.MakeAbsolute().ReadAllLines().Select(d => new NPath(d)));
                                NPath assetRootDirectory = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier);
                                commandLineArguments.AddRange(new List <string> {
                                    "--assetRootDirectory", assetRootDirectory.ToString(), "--assetManifest", exportManifest.ToString()
                                });
                            }
                            Backend.Current.AddAction(
                                actionName: "Package Shell File",
                                targetFiles: new NPath[] { tinyShellPath },
                                inputs: inputs.ToArray(),
                                executableStringFor: TinyEmscripten.NodeExe.InQuotes(),
                                commandLineArguments: commandLineArguments.Select(d => d.InQuotes()).ToArray()
                                );
                            Backend.Current.AddDependency(deployedGame.Path, tinyShellPath);
                        }
                        else if (!templateFilePath.HasExtension("meta"))
                        {
                            var targetPath = deployPath.Combine(fileRelativePath);
                            CopyTool.Instance().Setup(targetPath, templateFilePath);
                            Backend.Current.AddDependency(deployedGame.Path, targetPath);
                        }
                    }
                }
                else
                {
                    deployedGame = builtNativeProgram.DeployTo(deployPath);
                }

                entryPointExecutable = deployedGame.Path;
                if (config.EnableManagedDebugging && !(builtNativeProgram is IPackagedAppExtension))
                {
                    Backend.Current.AddDependency(deployedGame.Path, Il2Cpp.CopyIL2CPPMetadataFile(deployPath, setupGame));
                }

                // make sure http-server gets fetched from stevedore.  this should probably go elsewhere, but this is
                // a convenient quick hack place.
                if (config.PlatformBuildConfig is WebBuildConfig)
                {
                    var httpserver = new StevedoreArtifact("http-server");
                    httpserver.GenerateUnusualPath();
                    var httpserverpath = httpserver.GetUnusualPath().Combine("bin", "http-server");
                    Backend.Current.AddDependency(deployedGame.Path, httpserverpath);
                }
            }
            else
            {
                deployedGame = setupGame.DeployTo(deployPath);

                var dotNetAssembly = (DotNetAssembly)deployedGame;

                //Usually a dotnet runtime game does not have a static void main(), and instead references another "entrypoint asmdef" that provides it.
                //This is convenient, but what makes it weird is that you have to start YourEntryPoint.exe  instead of YourGame.exe.   Until we have a better
                //solution for this, we're going to copy YourEntryPoint.exe to YourGame.exe, so that it's easier to find, and so that when it runs and you look
                //at the process name you understand what it is.
                if (deployedGame.Path.HasExtension("dll"))
                {
                    var to = deployPath.Combine(deployedGame.Path.ChangeExtension("exe").FileName);
                    // Do an explicit check for the entrypoint.exe as a program may refer to other exes as assembly references
                    var from = dotNetAssembly.RecursiveRuntimeDependenciesIncludingSelf.SingleOrDefault(a => a.Path.FileName == "Unity.Runtime.EntryPoint.exe")?.Path;
                    if (from == null)
                    {
                        throw new InvalidProgramException($"Program {dotNetAssembly.Path} is an executable-like thing, but doesn't reference anything with Main");
                    }
                    Backend.Current.AddDependency(deployedGame.Path, CopyTool.Instance().Setup(to, from));
                    entryPointExecutable = to;
                }
                else
                {
                    entryPointExecutable = deployedGame.Path;
                }
            }

            //Because we use multidag, and try to not run all the setupcode when we just want to create projectfiles, we have a bit of a challenge.
            //Projectfiles require exact start and build commands. So we need to have a cheap way to calculate those. However, it's important that they
            //exactly match the actual place where the buildprogram is going to place our files. If these don't match things break down. The checks
            //in this block, they compare the "quick way to determine where the binary will be placed, and what the start executable is",  with the
            //actual return values returned from .DeployTo(), when we do run the actual buildcode.
            NPath deployedGamePath = GameDeployBinaryFor(gameProgram, config);

            //Identifier with slash means that this is complementary target and we should skip steps which are main target specific.
            //See comment in DotsConfigs.cs DotsConfigs.MakeConfigs() method for details.
            if (config.Identifier.IndexOf('/') != -1)
            {
                continue;
            }

            if (deployedGame.Path != deployedGamePath)
            {
                throw new InvalidProgramException($"We expected deployPath to be {deployedGamePath}, but in reality it was {deployedGame.Path}");
            }
            var expectedEntryPointExecutable = EntryPointExecutableFor(gameProgram, config);
            if (entryPointExecutable != expectedEntryPointExecutable)
            {
                throw new InvalidProgramException($"We expected entryPointExecutable to be {expectedEntryPointExecutable}, but in reality it was {entryPointExecutable}");
            }

            Backend.Current.AddAliasDependency(config.Identifier, deployedGamePath);
        }

        return(gameProgram);
    }
        private NPath GenerateXCodeProject(NPath mainLibPath)
        {
            var outputPath      = mainLibPath.Parent;
            var iosPlatformPath = AsmDefConfigFile.AsmDefDescriptionFor("Unity.Build.iOS.DotsRuntime").Path.Parent;
            var xcodeSrcPath    = iosPlatformPath.Combine(TinyProjectName + "~");
            var xcodeprojPath   = outputPath.Combine($"{TinyProjectName}.xcodeproj");

            // copy and patch pbxproj file
            var pbxPath         = xcodeprojPath.Combine("project.pbxproj");
            var pbxTemplatePath = xcodeSrcPath.Combine($"{TinyProjectName}.xcodeproj", "project.pbxproj");
            var result          = SetupXCodeProject(pbxTemplatePath);

            Backend.Current.AddWriteTextAction(pbxPath, result);
            m_projectFiles.Add(pbxPath);
            m_projectFiles.Add(mainLibPath);

            // copy and patch xcscheme file
            var xcschemePath         = xcodeprojPath.Combine("xcshareddata", "xcschemes", "Tiny-iPhone.xcscheme");
            var xcschemeTemplatePath = xcodeSrcPath.Combine($"{TinyProjectName}.xcodeproj", "xcshareddata", "xcschemes", "Tiny-iPhone.xcscheme");

            result = SetupXcScheme(xcschemeTemplatePath, m_config == DotsConfiguration.Release);
            Backend.Current.AddWriteTextAction(xcschemePath, result);
            m_projectFiles.Add(xcschemePath);

            // copy and patch Info.plist file
            var plistPath         = outputPath.Combine("Sources", "Info.plist");
            var plistTemplatePath = xcodeSrcPath.Combine("Sources", "Info.plist");

            result = SetupInfoPlist(plistTemplatePath);
            Backend.Current.AddWriteTextAction(plistPath, result);
            m_projectFiles.Add(plistPath);

            // copy xcodeproj files
            foreach (var r in xcodeSrcPath.Files(true))
            {
                if (r.Extension != "pbxproj" && r.Extension != "xcscheme" && r.FileName != "Info.plist" && !r.HasDirectory("AppIcon.appiconset"))
                {
                    var destPath = outputPath.Combine(r.RelativeTo(xcodeSrcPath));
                    destPath = CopyTool.Instance().Setup(destPath, r);
                    m_projectFiles.Add(destPath);
                }
            }

            // copy icon files
            var icons = IOSAppToolchain.Config.Icons;

            CopyIcon(xcodeSrcPath, outputPath, "Icon-iPhone-120.png", icons.iPhone2x);
            CopyIcon(xcodeSrcPath, outputPath, "Icon-iPhone-180.png", icons.iPhone3x);
            CopyIcon(xcodeSrcPath, outputPath, "Icon-iPad-152.png", icons.iPad2x);
            CopyIcon(xcodeSrcPath, outputPath, "Icon-iPad-167.png", icons.iPadPro2x);
            CopyIcon(xcodeSrcPath, outputPath, "AppStore-1024.png", icons.AppStore);
            CopyIcon(xcodeSrcPath, outputPath, "Contents.json", null);

            for (int i = 0; i < Deployables.Length; ++i)
            {
                var r = Deployables[i];
                if (r is DeployableFile && r.Path.FileName == "testconfig.json")
                {
                    var path = CopyTool.Instance().Setup(outputPath.Combine(r.Path.FileName), r.Path);
                    m_projectFiles.Add(path);
                    break;
                }
            }
            return(pbxPath);
        }
예제 #26
0
        private NPath PackageApp(NPath buildPath, NPath mainLibPath)
        {
            var deployedPath = buildPath.Combine(m_gameName + ".apk");

            if (m_apkToolchain == null)
            {
                Console.WriteLine($"Error: not Android APK toolchain");
                return(deployedPath);
            }

            var gradleProjectPath = mainLibPath.Parent.Parent.Parent.Parent.Parent;
            var pathToRoot        = new NPath(string.Concat(Enumerable.Repeat("../", gradleProjectPath.Depth)));
            var apkSrcPath        = AsmDefConfigFile.AsmDefDescriptionFor("Unity.Platforms.Android").Path.Parent.Combine("AndroidProjectTemplate~/");

            var javaLaunchPath         = m_apkToolchain.JavaPath.Combine("bin").Combine("java");
            var gradleLaunchPath       = m_apkToolchain.GetGradleLaunchJarPath();
            var releaseApk             = m_codeGen == CodeGen.Release;
            var gradleCommand          = releaseApk ? "assembleRelease" : "assembleDebug";
            var deleteCommand          = Unity.BuildTools.HostPlatform.IsWindows ? $"del /f /q {deployedPath.InQuotes(SlashMode.Native)} 2> nul" : $"rm -f {deployedPath.InQuotes(SlashMode.Native)}";
            var gradleExecutableString = $"{deleteCommand} && cd {gradleProjectPath.InQuotes()} && {javaLaunchPath.InQuotes()} -classpath {gradleLaunchPath.InQuotes()} org.gradle.launcher.GradleMain {gradleCommand} && cd {pathToRoot.InQuotes()}";

            var apkPath = gradleProjectPath.Combine("build/outputs/apk").Combine(releaseApk ? "release/gradle-release.apk" : "debug/gradle-debug.apk");

            Backend.Current.AddAction(
                actionName: "Build Gradle project",
                targetFiles: new[] { apkPath },
                inputs: m_apkToolchain.RequiredArtifacts.Append(mainLibPath).Concat(m_supportFiles.Select(d => d.Path)).ToArray(),
                executableStringFor: gradleExecutableString,
                commandLineArguments: Array.Empty <string>(),
                allowUnexpectedOutput: false,
                allowedOutputSubstrings: new[] { ":*", "BUILD SUCCESSFUL in *" }
                );

            var templateStrings = new Dictionary <string, string>
            {
                { "**TINYNAME**", m_gameName.Replace("-", "").ToLower() },
                { "**GAMENAME**", m_gameName },
            };

            // copy and patch project files
            foreach (var r in apkSrcPath.Files(true))
            {
                var destPath = gradleProjectPath.Combine(r.RelativeTo(apkSrcPath));
                if (r.Extension == "template")
                {
                    destPath = destPath.ChangeExtension("");
                    var code = r.ReadAllText();
                    foreach (var t in templateStrings)
                    {
                        if (code.IndexOf(t.Key) != -1)
                        {
                            code = code.Replace(t.Key, t.Value);
                        }
                    }
                    Backend.Current.AddWriteTextAction(destPath, code);
                }
                else
                {
                    destPath = CopyTool.Instance().Setup(destPath, r);
                }
                Backend.Current.AddDependency(apkPath, destPath);
            }

            var localProperties = new StringBuilder();

            localProperties.AppendLine($"sdk.dir={m_apkToolchain.SdkPath}");
            localProperties.AppendLine($"ndk.dir={m_apkToolchain.Sdk.Path.MakeAbsolute()}");
            var localPropertiesPath = gradleProjectPath.Combine("local.properties");

            Backend.Current.AddWriteTextAction(localPropertiesPath, localProperties.ToString());
            Backend.Current.AddDependency(apkPath, localPropertiesPath);

            // copy additional resources and Data files
            // TODO: better to use move from main lib directory
            foreach (var r in m_supportFiles)
            {
                var targetAssetPath = gradleProjectPath.Combine("src/main/assets");
                if (r.Path.FileName == "testconfig.json")
                {
                    targetAssetPath = buildPath.Combine(r.Path.FileName);
                }
                else if (r is DeployableFile && (r as DeployableFile).RelativeDeployPath != null)
                {
                    targetAssetPath = targetAssetPath.Combine((r as DeployableFile).RelativeDeployPath);
                }
                else
                {
                    targetAssetPath = targetAssetPath.Combine(r.Path.FileName);
                }
                Backend.Current.AddDependency(apkPath, CopyTool.Instance().Setup(targetAssetPath, r.Path));
            }

            return(CopyTool.Instance().Setup(deployedPath, apkPath));
        }